sobota, 15 grudnia 2012

Różnice między klasą abstrakcyjną, a interfejsem

wstęp

Zdaję sobie sprawę, że o różnicach pomiędzy interfejsem, a klasą abstrakcyjną pisałem już wielokrotnie (m. in. "klasa, czy już interfejs" oraz "abstrakcja vs interfejs"), jednak nigdy nie pisałem o tych podstawowych. Opierając się na wielu rozmowach rekrutacyjnych, które do tej pory przeprowadziłem, zauważyłem, że te różnice nie są jednak tak powszechnie znane, jak przypuszczałem i może warto byłoby je wymienić oraz wytłumaczyć skąd się biorą.

Dlatego też dzisiaj chciałbym nadrobić brak wpisu na ten temat:)

dziedziczenie

Jedną z podstawowych różnic pomiędzy interfejsem, a klasą abstrakcyjną (klasami w ogóle) jest możliwość dziedziczenia - klasa może implementować wiele interfejsów, ale rozszerzać jednego rodzica.

Wielokrotne dziedziczenie klasowe to funkcjonalność, która w wielu językach programowania nie jest w ogóle możliwa. Dlaczego? Ponieważ generuje więcej problemów niż pożytku.
Gdy klasa ma wielu rodziców, a w każdym istnieje metoda o takiej samej deklaracji, to musielibyśmy określić, którą my jesteśmy zainteresowani. Taki kod byłby ciężki w zarządzaniu, ponieważ trzeba się uważnie w niego zagłębić podczas ewentualnych zmian, aby przypadkiem czegoś nie zepsuć.

A może jednak by się coś takiego przydało? No cóż, raczej nie. Jakiś czas temu miała miejsce (dość krótką) dyskusja w serwisie Goldenline na temat tego, czy taka funkcjonalność ma zastosowanie w projektach. Okazuje się, że raczej nikt jej nie uwzględnia i nikt nie natrafił na problem, którego nie dałoby się rozwiązać bez niej.

Skoro jednak wielodziedziczenie klasowe jest złe, to dlaczego można implementować wiele interfejsów?
Powód jest prosty - interfejs opiera się na funkcjonalności, a klasa na implementacji. Tak więc, nawet jeżeli implementujemy dziesięć interfejsów i każdy posiada taką samą deklarację jednej metody, to nie kolidują one wzajemnie ze sobą. Interfejs jest gwarantem jej istnienia, a nie tego, w jaki sposób powinna być zaimplementowana.

widoczność metod

Wszystkie zadeklarowane metody w interfejsie są publiczne, natomiast metody zadeklarowane w klasach mogą być publiczne lub chronione.

Interfejs jest gwarantem funkcjonalności, gwarancją tego, że klasa implementująca go będzie posiadała określone metody. Dlatego też tylko publiczne deklaracje mogą wystąpić w interfejsie. Pozostałe nas w tym momencie nie interesują, ponieważ są związane z implementacją.

W kwestii klas jest trochę inaczej, ponieważ możemy posiadać grupę klas, które różnią się tylko kilkoma rzeczami, ale poza tym są całkiem do siebie podobne, a deklaracje ich metod publicznych (niekoniecznie wszystkich) - identyczne. W takim wypadku, możemy zadeklarować metodę chronioną, w której ciele będzie kod odpowiedzialny za te różnice (jednym z wzorców wykorzystujących takie zachowanie jest Template Method).

deklaracja, a definicja

Interfejs może zawierać jedynie deklaracje metod, a klasa abstrakcyjna może zawierać również metody zdefiniowane.

Wynika to bezpośrednio z tego, że interfejs skupia się na zapewnieniu określonej funkcjonalności, natomiast klasa abstrakcyjna może również opierać się na podobieństwie w implementacji klas pochodnych.

stałe

Zarówno w interfejsach, jak i klasach można definiować stałe, a wynika to z faktu, że stałe są niezależne od konkretnej instancji.

atrybuty

Klasy abstrakcyjne mogą zawierać atrybuty, natomiast interfejsy - nie. Powód jest ten sam, jak przy deklaracjach i definicjach metod.

podsumowanie

Starałem się, poza wymienieniem różnic pomiędzy interfejsem, a klasą abstrakcyjną, wyjaśnić również skąd się one biorą, że nie jest to jedynie wymysł twórców języków obiektowych, a wynika to bezpośrednio z idei samych konstrukcji.

Mam nadzieję, że nie zapomniałem o niczym, ale jeżeli by się tak stało, to piszcie w komentarzach, a ja postaram się usunąć błędy, jak najszybciej:)