sobota, 28 września 2013

Whatever

świętymi bądźmy... ale trzeba znać umiar

Ostatnimi czasy zdarzało mi się trafiać w review kodu na zażarte dyskusje na temat pewnego jego fragmentu. Czasami rozmowy tam prowadzone zmieniały się w dialogi nie za pośrednictwem komentarzy i klawiatur, a werbalne.
Gdy się wsłuchiwałem (bądź wczytywałem) w nie, ciężko było trzymać stronę któregokolwiek z dyskutantów, a to dlatego, że moim zdaniem każdy z nich miał rację, a rozmowa sprowadzała się do kwestii estetycznych i osobistych preferencji aniżeli do "obrony jakości kodu" przed "nieodpowiednimi" linijkami.

No i tak sobie te dyskusje trwały do momentu, gdy w końcu obaj rozmówcy decydowali się na skorzystanie z "telefonu do przyjaciela", co sprowadzało się do skierowania spojrzeń w kierunku najbliższego programisty i zadania mu jakże istotnego pytania - "A Ty co o tym sądzisz?".

Zdarzało się tak, że owym programistą byłem ja. I cóż mogłem odpowiedzieć?

dobry kod ma wiele postaci

Prawda jest taka, że dziesięciu dobrych programistów, którzy znają i poprawnie wykorzystują wedle potrzeby wzorce, praktyki i techniki, nie napiszą takiego samego kodu. Czy to znaczy, że któryś z nich jest gorszy (i kod i programista)? Nie.

Wiele osób porównuje programowanie do pewnego rodzaju sztuki i muszę przyznać, że coś w tym jest. Tak jak wielu było dobrych impresjonistów (grupa artystów, których łączą pewne wspólne elementy zarówno w kwestii treści jak i formy), którzy choćby malowali to samo z pewnością powstałby zupełnie inny obraz i zarówno jeden jak i drugi miałby swoich zwolenników. Tak samo w programowaniu - dwóch programistów korzysta z tego samego języka, z tych samych praktyk i wzorców, stosuje te same techniki, ale ich kod się różni, co wcale nie sprawia, że któryś nie jest dobry.

To wszystko sprowadza się do estetyki i osobistych standardów kodowania. Chociaż byśmy pracowali w jednym zespole i mieli jakieś wspólnie wymyślone standardy, to i tak zawsze pozostaje w kodzie miejsce na pierwiastek "duszy" autora. I to nie jest problem, który należy rozwiązywać - to jest wielka zaleta naszej pracy.

pokaż mi kod, a powiem Ci kim jesteś

Poniżej przedstawiam trzy przykłady, gdzie każdy przykład składa się z dwóch rozwiązań tego samego problemu. Czy któryś jest lepszy? Nie sądzę. Oczywiście sam mam swoje preferencje, ale nie znaczy to, że wysilałbym palce na pisanie komentarzy komuś, kto wybrał inne (równie dobre) rozwiązanie.
Od razu przepraszam, że nie podałem rzeczywistych przykładów, ale przypuszczam, że podobne konstrukcje widzieliście już tyle razy, że nie ma potrzeby.

Wyobraźcie sobie, że mamy klasę abstrakcyną, która deklaruje abstrakcyjną metodą mającą zwracać jakiś obiekt:
public abstract class SomeAbstract {

     /**
       * really great code
       */ 

     abstract protected Collection getCollection();
}

Poniżej przedstawiam dwa sposoby implementacji metody w konkretnej klasie.
Lepiej jest tak:
@Override 
protected Collection getCollection() {
     Collection collection = new Collection();
  
     collection.addItem(new ItemA());
     collection.addItem(new ItemB());
  
     return collection;
}
czy może tak:
@Override 
protected Collection getCollection() {
     return CollectionFactory.create();
}


Kolejny przykład:
public boolean foo() {
     boolean isValid = isValid();
  
     if (isValid) {
          // do something
     }
     else {
          // do something else
     }
  
     return isValid;
}

czy lepiej tak:
 
public boolean foo() {
     if (isValid()) {
          // do something
          return true;
     }
  
     // do something else
     return false;
}


I ostatni:
public int foo(boolean success) {
     if (success) {
          return getShortInt();
     }
  
     return getLongInt();
}

albo:
  
public int foo(boolean success) {
     return success ? getShortInt() : getLongInt();
}


I wiele, wiele więcej, ale myślę, że już wiadomo o co chodzi:)
Czy któryś jest lepszy od drugiego? Pewnie każdy z Was mimowolnie wybrał już swoje typy, ale prawda jest taka, że w każdym przypadku to tak naprawdę bez różnicy.

ale to przecież detale

Zgadzam się, że powyższe przykłady nie prezentują jakichś wyszukanych różnic, ale im więcej metod/klas, im większa część funkcjonalności lub jej całość jest do zrobienia w zakresie pojedynczego zadania, tym więcej takich detali, które niekiedy składają się na całkiem odmienne rozwiązania.
Tu nie ma potrzeby dyskutować i kogoś przekonywać do swojego rozwiązania, ponieważ taka rozmowa jest bezcelowa. Dlaczego? Bo argumenty zarówno Twoje, jak i Twojego rozmówcy będą się równoważyć, a sporowadza się to mniej więcej do tego, czy lepiej pisać na czarnej czy białej klawiaturze. Można to skwitować jednym słowem - whatever :)