Jeśli masz klasę, która dziedziczy z dwóch odrębnych klas, czy nie oznacza to, że twoja podklasa automatycznie (przynajmniej) 2 rzeczy, po jednej z każdej nadklasy?
Uważam, że nie ma różnicy, jeśli masz wiele interfejsów dziedziczenia.
Edycja: Dla jasności uważam, że jeśli podklasowanie wielu klas narusza SRP, to implementacja wielu interfejsów (niemarkerowych lub podstawowego interfejsu (np. Porównywalny)) również narusza SRP.
Odpowiedzi:
W bardzo wąskim znaczeniu odpowiedź brzmi „Tak”: zakładając, że twoje podstawowe klasy lub interfejsy są zaprojektowane do jednego celu, dziedziczenie obu z nich tworzy klasę z wieloma obowiązkami. Jednak to, czy jest to „zła rzecz”, zależy od charakteru odziedziczonych klas lub interfejsów.
Możesz podzielić swoje klasy i interfejsy na dwie główne grupy - te dotyczące zasadniczej złożoności systemu i te dotyczące jego przypadkowej złożoności. Jeśli dziedziczy po więcej niż jednej klasie „zasadniczej złożoności”, jest źle; jeśli odziedziczysz po jednej „niezbędnej” i jednej lub więcej „przypadkowych” klasach, to jest OK.
Na przykład w systemie rozliczeniowym możesz mieć klasy do reprezentowania faktur i cykli rozliczeniowych (dotyczą one zasadniczej złożoności) oraz klasy do utrwalania obiektów (dotyczą przypadkowej złożoności). Jeśli odziedziczysz w ten sposób
to źle: ponosisz
BillingCycleInvoice
mieszaną odpowiedzialność, ponieważ dotyczy to zasadniczej złożoności systemu.Z drugiej strony, jeśli odziedziczysz w ten sposób
Twoja klasa jest w porządku: technicznie obsługuje dwa problemy naraz, ale ponieważ tylko jeden z nich jest niezbędny, możesz odpisać dziedziczenie przypadkowe jako „koszt prowadzenia działalności gospodarczej”.
źródło
SRP jest wskazówką, aby unikać klas boskich, które są złe, ale można je również traktować zbyt dosłownie, a projekt rozpoczyna balon z mnóstwem klas, które naprawdę nie mogą nic zrobić bez połączenia garści. Wielokrotne dziedziczenie / wiele interfejsów może być znakiem, że klasa staje się zbyt duża, ale może to również oznaczać, że twoje skupienie jest zbyt szczegółowe dla danego zadania i twoje rozwiązanie jest przeprojektowane, lub może być po prostu idealnie ważne zastosowanie wielokrotnego dziedziczenia, a twoje obawy są bezpodstawne.
Projektowanie oprogramowania jest tak samo sztuką, jak nauką, stanowi równowagę wielu konkurujących ze sobą zainteresowań. Mamy zasady, które pomagają unikać rzeczy, o których wiemy, że są zbyt daleko w jednym kierunku, który powoduje problemy, ale zasady te mogą równie łatwo doprowadzić nas do miejsca, które jest tak samo złe lub gorsze, niż to, czego staraliśmy się uniknąć w pierwszej kolejności.
źródło
Nieco. Skupmy się na głównej zasadzie SRP:
Wielokrotne dziedziczenie nie wymusza tego, ale prowadzi do tego. Jeśli klasa zastępuje lub implementuje swoje klasy podstawowe, jest to więcej powodów do zmiany. W ten sposób wielokrotne dziedziczenie interfejsu jest bardziej kłopotliwe niż dziedziczenie klas. Jeśli dwie klasy są dziedziczone, ale nie są zastępowane, wówczas powody zmiany istnieją w klasach podstawowych, a nie w nowej klasie.
Miejsca, w których wielokrotne dziedziczenie nie narusza SRP, to gdy wszystkie oprócz jednego z podstawowych typów nie są czymś, co klasa implementuje, ale działają jak cecha, którą ma klasa. Rozważ
IDisposable
w języku C #. Rzeczy, które są jednorazowe, nie mają dwóch obowiązków, interfejs działa tylko jako widok klas, które mają tę samą cechę, ale mają zdecydowanie różne obowiązki.źródło
Moim zdaniem nie. Dla mnie jednym obowiązkiem jest „modelować użytkownika mojej aplikacji”. Być może będę musiał dostarczyć te dane za pośrednictwem usługi sieci Web i przechowywać je w relacyjnej bazie danych. Mógłbym zapewnić tę funkcjonalność, dziedzicząc kilka klas wtyczek.
To nie znaczy, że klasa ma trzy obowiązki. Oznacza to, że część odpowiedzialności za modelowanie użytkownika wymaga wsparcia serializacji i trwałości.
źródło
Ani trochę. W Javie możesz mieć interfejs reprezentujący jakiś obiekt domeny - na przykład Foo. Może być konieczne porównanie z innymi obiektami Foo, a zatem implementuje funkcję Porównywalną (z logiką porównawczą uzewnętrznioną w klasie Komparator); może się zdarzyć, że ten obiekt również musi być szeregowalny, aby można go było przenosić przez sieć; i może wymagać klonowania. Zatem klasa implementuje trzy interfejsy, ale nie ma żadnej logiki w celu ich obsługi poza obsługą Foo.
To powiedziawszy, głupotą jest doprowadzenie SRP do skrajności. Jeśli klasa definiuje więcej niż jedną metodę, czy narusza ona SRP? Wszystkie obiekty Java mają metodę toString () i metodę równości (Object), co oznacza, że KAŻDY obiekt w Javie jest odpowiedzialny zarówno za równość, jak i autoprezentację. Czy to źle?
źródło
Myślę, że to zależy od tego, jak zdefiniujesz odpowiedzialność. W klasycznym przykładzie iostream nie narusza SRP. IOstream odpowiada za zarządzanie jednym strumieniem dwukierunkowym.
Po wyczerpaniu się prymitywnych obowiązków przechodzisz na bardziej ogólne obowiązki na wysokim szczeblu, w końcu jak określasz odpowiedzialność za swój główny punkt wejścia? Jego zadaniem musi być „bycie głównym punktem wejścia”, a nie „wszystko, co robi moja aplikacja”, w przeciwnym razie nie można nie naruszyć SRP i stworzyć aplikacji.
źródło