sobota, 27 października 2012

interfejs + unsupported method? WTF ?!

przesada jest twoim wrogiem

Żaden z nas nie posiadł całej wiedzy, którą posiada obecnie, wraz z pierwszym kontaktem z programowaniem obiektowym. To oczywiste, że wszystkiego trzeba się nauczyć. Trwa to dłużej lub krócej, uczymy się na własnych błędach, doświadczeniu innych itp.

Czasami odkrywamy "coś", co na zawsze zmienia nasz sposób postrzegania kodu. Zdarza się, że danego rozwiązania, konstrukcji etc. używaliśmy wcześniej, ale dopiero w pewnym momencie odkrywamy wszystkie tajemnice stojące za danym zagadnieniem, ich piękno i wartości. A przynajmniej tak nam się wydaje:)

Wiele osób w tym momencie wpada w pułapkę tj. zaczynają nadużywać danego rozwiązania, bo nagle dostrzegają setki miejsc, gdzie ono "idealnie pasuje". Niestety równie często, po pewnym czasie, okazuje się, że nie zawsze było tak idealnie, jak sądziliśmy.

ostatnio refaktoryzowałem kod...

Ostatnio miałem "przyjemność" refaktoryzować pewien kod. W pewnym momencie, przeglądając niektóre klasy, zauważyłem dość interesujące metody:
public function foo() 
{ 
  throw new Exception('Unsupported method!');
}

Moje zdziwienie było wielkie. Któż miał potrzebę implementować metodę, która wyrzuca wyjątek przy każdym jej wywołaniu? Jaka idea przyświecała autorowi natchnionemu?

Nauczony jednak doświadczeniem, że nie wszystko, czego nie rozumiem i nie mogę pojąć, oznacza coś złego, zacząłem przyglądać się temu tworowi bliżej. Dość szybko okazało się, że stworzenie takiej metody miało "sens". Otóż cała klasa implementowała interfejs, który wymagał istnienia trzech metod, między innymi owej foo(), ale ponieważ ta klasa nie potrzebowała tej metody (a musiała implementować interfejs, bo tak były zaprojektowane zależności pomiędzy obiektami), więc takie wyjście wydało się programiście słuszne.

wtf?

Czy to było dobre rozwiązanie? Oczywiście, że nie. Ba, przede wszystkim sam projekt funkcjonalności jest błędny, ponieważ wszystko działało nawet w przypadku, gdy jedna z metod wymaganych (bo o to chodzi, gdy się używa interfejsów) wyrzucała wyjątek! Więc po co on (interfejs) tam został wrzucony? Jeżeli tworzymy interfejs i funkcjonalność nie potrzebuje wszystkich jego metod to znaczy, że coś robimy źle, to znaczy, że zależności są totalnie spieprzone!

Jaki ma to związek ze wstępem? Otóż funkcjonalność, przez którą się wtedy przedarłem była pełna tego typu konstrukcji i przesycona interfejsami, więc zakładam, że osoba, która była odpowiedzialna za dany kod dopiero rozpoczynała swoją przygodę z interfejsami. Dostrzegała ich wielki potencjał, ale jeszcze nie do końca była w stanie go okiełznać. I właśnie dlatego należy być bardzo ostrożnym w korzystaniu z narzędzi, technik etc., które dopiero stają się dla nas zrozumiałe, ponieważ często prowadzi to do takich lub podobnych konstrukcji.

wniosek jest jeden

Warto dbać o poprawne zależności pomiędzy klasami i warto opierać te zależności na interfejsach jednak, przede wszystkim, trzeba to robić mądrze.
Interfejs gwarantuje nam implementację metod, które są w nim zadeklarowane, więc jeżeli okazuje się, że nie wszystkie metody są potrzebne oznacza to, że dochodzimy do sprzeczności - wymagamy obecności metod opcjonalnych.

Pamiętajcie - ALBO coś jest wymagane ALBO opcjonalne. Interfejs jest od tego, aby zagwarantować istnienie wymaganego i jeżeli okazuje się, że pewna metoda wcale wymagana nie jest, jest to równoznaczne z tym, że interfejs jest nam niepotrzebny. Albo jest potrzebny, ale nie taki, jaki stworzyliśmy.