Kiedy używać interfejsów lub klas abstrakcyjnych? Kiedy używać obu?
Podczas gdy niektóre wytyczne mówią, że należy użyć interfejsu, aby zdefiniować umowę dla klasy, w której dziedziczenie nie jest jasne (IDomesticated
) i dziedziczenia, gdy klasa jest rozszerzeniem innej (Cat : Mammal
, Snake : Reptile
), zdarzają się przypadki, kiedy (moim zdaniem) te wytyczne wchodzą w szary obszar.
Powiedzmy na przykład, że moje wdrożenie byłoCat : Pet
. Pet
jest klasą abstrakcyjną. Powinny zostać rozszerzone naCat : Mammal, IDomesticated
gdzieMammal
jest klasą abstrakcyjną iIDomesticated
to jest interfejs? Czy jestem w konflikcie zPOCAŁUNEK/YAGNI zasady (nawet jeśli nie jestem pewien, czy będzieWolf
klasa w przyszłości, która nie byłaby w stanie dziedziczyć po niejPet
)?
Odchodząc od metaforycznegoCat
s iPet
s, powiedzmy, że mam kilka klas reprezentujących źródła danych przychodzących. Wszyscy muszą jakoś wdrożyć tę samą bazę. Mógłbym zaimplementować jakiś ogólny kod w abstrakcjiSource
klasa i dziedziczę po niej. Mógłbym też po prostu zrobićISource
interfejs (który jest dla mnie bardziej „właściwy”) i ponownie implementuje kod ogólny w każdej klasie (co jest mniej intuicyjne). Wreszcie mogłem „zjeść ciasto i zjeść je”, tworząc zarówno klasę abstrakcyjną, jak i interfejs. Co jest najlepsze?
Te dwa przypadki wywołują punkty za używanie tylko klasy abstrakcyjnej, tylko interfejsu i używanie zarówno klasy abstrakcyjnej, jak i interfejsu. Czy są to wszystkie ważne wybory, czy też istnieją „zasady”, kiedy ktoś powinien być używany nad innym?
Chciałbym wyjaśnić, że „używając zarówno klasy abstrakcyjnej, jak i interfejsu”, która obejmuje przypadek, gdy zasadniczo reprezentują to samo (Source
iISource
oba mają tych samych członków), ale klasa dodaje ogólną funkcjonalność, podczas gdy interfejs określa umowę.
Warto również zauważyć, że to pytanie dotyczy głównie języków, które nie obsługują wielokrotnego dziedziczenia (takich jak .NET i Java).