Dobrze znaną wadą tradycyjnych hierarchii klas jest to, że są one złe, jeśli chodzi o modelowanie świata rzeczywistego. Na przykład próba przedstawienia gatunku zwierząt za pomocą klas. W rzeczywistości robi to kilka problemów, ale jednym z nich, którego nigdy nie widziałem, jest rozwiązanie, gdy podklasa „traci” zachowanie lub właściwość zdefiniowaną w superklasie, na przykład pingwina niezdolnego do latania (tam są prawdopodobnie lepszymi przykładami, ale to pierwszy, który przychodzi mi do głowy).
Z jednej strony nie chcesz definiować, dla każdej właściwości i zachowania, jakiejś flagi, która określa, czy jest ona w ogóle obecna, i sprawdzaj ją za każdym razem przed uzyskaniem dostępu do tego zachowania lub właściwości. Chciałbyś tylko powiedzieć, że ptaki mogą latać, prosto i wyraźnie, w klasie Bird. Ale wtedy byłoby miło, gdyby można było później zdefiniować „wyjątki”, bez konieczności wszędzie używania okropnych hacków. Zdarza się to często, gdy system jest produktywny przez pewien czas. Nagle znajdujesz „wyjątek”, który w ogóle nie pasuje do oryginalnego projektu i nie chcesz zmieniać dużej części kodu, aby go dostosować.
Czy istnieją jakieś wzorce językowe lub projektowe, które mogą w czysty sposób poradzić sobie z tym problemem, nie wymagając poważnych zmian w „superklasie” i całym kodzie, który z niej korzysta? Nawet jeśli rozwiązanie obsługuje tylko konkretny przypadek, kilka rozwiązań może razem stanowić kompletną strategię.
Po dłuższym zastanowieniu zdaję sobie sprawę, że zapomniałem o zasadzie substytucji Liskowa. Dlatego nie możesz tego zrobić. Zakładając, że zdefiniujesz „cechy / interfejsy” dla wszystkich głównych „grup funkcji”, możesz swobodnie wdrażać cechy w różnych gałęziach hierarchii, tak jak cecha Latanie może być zaimplementowana przez Ptaki, a także specjalne rodzaje wiewiórek i ryb.
Więc moje pytanie może brzmieć: „Jak mogę cofnąć wdrożenie cechy?” Jeśli twoją nadklasą jest Java Serializable, musisz też nią być, nawet jeśli nie ma możliwości serializacji swojego stanu, na przykład jeśli zawierasz „Socket”.
Jednym ze sposobów na to jest zawsze zdefiniowanie wszystkich swoich cech od początku w parach: Latanie i Niepowodzenie (co wyrzuciłoby UnsupportedOperationException, jeśli nie jest sprawdzone). Nie-cecha nie definiuje żadnego nowego interfejsu i można ją po prostu sprawdzić. Brzmi jak „tanie” rozwiązanie, zwłaszcza jeśli jest stosowane od samego początku.
" it would be nice if one could define "exceptions" afterward, without having to use some horrible hacks everywhere"
czy uważasz, że metoda fabryczna kontrolująca zachowanie jest nieuczciwa?
NotSupportedException
z Penguin.fly()
.
class Penguin < Bird; undef fly; end;
. Czy powinieneś to inne pytanie.
function save_yourself_from_crashing_airplane(Bird b) { f.fly() }
byłoby to znacznie bardziej skomplikowane. (jak powiedział Peter Török, narusza LSP)