piątek, 18 września 2009

Design by contract i wstępna walidacja danych, część 3

Mając przygotowany już kod obsługujący naszą składnię, możemy przejść analizy przekazanego wyrażenia.

Korzeniem wyrażenia jest zawsze obiekt LambdaExpression, którego Body jest wywołaniem metody lub konstruktora, których argumenty będą poddane sprawdzeniu. Wywołanie metody powoduje odłożenie na drzewie obiektu MethodCallExpression, zaś sama metoda i informaje o niej (MethodInfo) jest dostępna przez property Method. Wywołanie konstruktora (np. new Service(...)) odkłada na drzewie obiekt NewExpression, umieszczając informacje o konstruktorze (ConstructorInfo) w property Constructor.

środa, 16 września 2009

Design by contract i wstępna walidacja danych, część 2

W części pierwszej poznaliśmy założenia zadania. Tym razem zaczniemy budować implementację. W tym miejscu wspomnę, że w przeważającej większości zabaw z wyrażeniami lambda oraz składnią imitującą język naturalny warto najpierw napisać przykładowe użycie kodu (jak to zrobiliśmy wcześniej), a dopiero później próbować zrealizować pomysł.

Korzystamy z funkcji rozszerzających, więc pierwszą czynnością jest przygotowanie statycznej klasy, w której zamieścimy implementację - nie ma tu nic odkrywczego:

wtorek, 15 września 2009

Design by contract i wstępna walidacja danych, część 1

Jednym z założeń wzorca Design by contract jest sprawdzanie poprawności przekazanych do funkcji argumentów. Zapewne w wielu miejscach Twojego kodu możesz spotkać konstrukcje podobne do tej poniżej:
public void PerformAction(string actionName, Data data, int id)
{
  if (string.IsNullOrEmpty(actionName))
  {
    throw new ArgumentNullException("actionName");
  }
  if (data == null)
  {
    throw new ArgumentNullException("data");
  }
  if (id >= 50)
  {
    throw new ArgumentOutOfRangeException("id", "Must be less than 50");
  }
   
  ...
}

W powyższym przykładzie dostrzegam następującego zagrożenia:
  • powtórzenie nazw argumentów - raz w deklaracji argumentów funkcji, drugi raz w sprawdzeniu warunku null, trzeci raz w rzucaniu wyjątku
  • rozwlekłość, a przez to zmniejszona czytelność kodu
  • przy rosnącej ilości funkcji da się zaobserwować praktycznie identyczne sprawdzenia
Z ostatnim punktem związane jest utrudnione zarządzanie kodem w przyszłości - wystarczy sobie zadać pytanie - a jeśli zamiast ArgumentNullException będę chciał rzucić inny wyjątek? A jeśli będę potrzebował innej walidacji? A jeśli dodatkowo chciałbym logować wartości przekazanych argumentów?