Mam nadzieję, że nie jest zbyt akademicki ...
Powiedzmy, że potrzebuję prawdziwych i złożonych liczb w mojej bibliotece SW.
Na podstawie relacji is-a (lub tutaj ) liczba rzeczywista jest liczbą zespoloną, gdzie b w urojonej części liczby zespolonej wynosi po prostu 0.
Z drugiej strony, moja implementacja byłaby taka, że dziecko rozszerza element nadrzędny, więc w elemencie RealNumber miałbym prawdziwą część, a element potomny ComplexNumber dodałby sztukę wymyśloną.
Istnieje również opinia, że dziedzictwo jest złe .
Pamiętam, jak wczoraj, kiedy uczyłem się OOP na uniwersytecie, mój profesor powiedział, że to nie jest dobry przykład dziedziczenia, ponieważ wartość bezwzględna tych dwóch jest obliczana inaczej (ale do tego mamy przeciążenie metody / polimorfizm, prawda?) .. .
Z mojego doświadczenia wynika, że często używamy dziedziczenia do rozwiązywania problemu DRY, w wyniku czego często mamy sztuczne abstrakcyjne klasy w hierarchii (często mamy problem ze znalezieniem nazw, ponieważ nie reprezentują one obiektów z prawdziwego świata).
źródło
Odpowiedzi:
Nawet jeśli w sensie matematycznym liczba rzeczywista jest liczbą zespoloną, nie jest dobrym pomysłem wyprowadzać rzeczywistą liczbę złożoną. Jest to sprzeczne z zasadą podstawienia Liskowa, mówiącą (między innymi), że klasa pochodna nie powinna ukrywać właściwości klasy podstawowej.
W takim przypadku liczba rzeczywista musiałaby ukryć urojoną część liczby zespolonej. Oczywiste jest, że nie ma sensu przechowywać ukrytej liczby zmiennoprzecinkowej (części urojonej), jeśli potrzebujesz tylko prawdziwej części.
Jest to w zasadzie ten sam problem, co przykład prostokąta / kwadratu wspomniany w komentarzu.
źródło
W rzeczywistości nie jest to istotny powód przeciwko wszelkiemu dziedzictwu, tylko proponowany model
class RealNumber
<->class ComplexNumber
.Możesz rozsądnie zdefiniować interfejs
Number
, który zarównoRealNumber
i jak iComplexNumber
zaimplementuje.To może wyglądać
Ale wtedy chcesz ograniczyć inne
Number
parametry w tych operacjach, aby były tego samego typu pochodnegothis
, co, do którego możesz się zbliżyć za pomocąLub zamiast tego użyjesz języka, który dopuszcza polimorfizm strukturalny, zamiast polimorfizmu podtypu. W konkretnym przypadku liczb może być potrzebna tylko możliwość przeciążenia operatorów arytmetycznych.
źródło
Rozwiązanie: Nie masz
RealNumber
klasy publicznejUważałbym, że byłoby całkowicie OK, gdyby
ComplexNumber
dysponował statyczną metodą fabrycznąfromDouble(double)
, która zwróciłaby liczbę zespoloną z wyimaginowaną wartością zero. Następnie można użyć wszystkich operacji, które można wykonaćRealNumber
na instancji w tejComplexNumber
instancji.Ale mam problem ze zrozumieniem, dlaczego chcesz / musisz mieć
RealNumber
klasę odziedziczoną publicznie . Zwykle z tych powodów używa się dziedziczenia (z mojej głowy, popraw mnie, jeśli coś przegapiłem)przedłużenie zachowania.
RealNumbers
nie można wykonać żadnych dodatkowych operacji, których liczba złożona nie może zrobić, więc nie ma sensu tego robić.implementowanie abstrakcyjnych zachowań z konkretną implementacją. Ponieważ
ComplexNumber
nie powinno to być abstrakcyjne, nie dotyczy to również.ponowne użycie kodu. Jeśli użyjesz
ComplexNumber
klasy, ponownie wykorzystasz 100% kodu.bardziej konkretna / wydajna / dokładna implementacja dla konkretnego zadania. Można to tutaj zastosować,
RealNumbers
szybciej zaimplementować niektóre funkcjonalności. Ale wtedy ta podklasa powinna być ukryta za statycznąfromDouble(double)
i nie powinna być znana na zewnątrz. W ten sposób nie trzeba ukrywać części wyobrażonej. Na zewnątrz powinny być tylko liczby zespolone (którymi są liczby rzeczywiste). Możesz także zwrócić tę prywatną klasę RealNumber z dowolnych operacji w klasie liczb zespolonych, które dają liczbę rzeczywistą. (Zakłada się, że klasy są niezmienne, jak większość klas liczbowych).To jest jak zaimplementowanie podklasy Integer o nazwie Zero i zakodować niektóre operacje, ponieważ są one trywialne dla zera. Możesz to zrobić, ponieważ każde zero jest liczbą całkowitą, ale nie upubliczniaj, ukryj to za metodą fabryczną.
źródło
Mówienie, że liczba rzeczywista jest liczbą zespoloną, ma większe znaczenie w matematyce, zwłaszcza w teorii mnogości, niż w informatyce.
W matematyce mówimy:
Nie oznacza to jednak, że musisz, a nawet powinieneś używać dziedziczenia podczas projektowania biblioteki tak, aby zawierała klasy RealNumber i ComplexNumber. In Effective Java, drugie wydanie Joshua Bloch; Punkt 16 to „Preferuj kompozycję nad spadkiem”. Aby uniknąć problemów wymienionych w tym elemencie, po zdefiniowaniu klasy RealNumber można jej użyć w klasie ComplexNumber:
Dzięki temu możesz ponownie wykorzystać swoją klasę RealNumber, aby utrzymać kod w stanie SUCHO, unikając problemów zidentyfikowanych przez Joshua Blocha.
źródło
Istnieją tutaj dwa problemy. Po pierwsze, powszechne jest używanie tych samych terminów dla rodzajów kontenerów i rodzajów ich zawartości, szczególnie w przypadku prymitywnych typów, takich jak liczby. Termin ten jest
double
na przykład używany do opisania zarówno zmiennoprzecinkowej podwójnej precyzji, jak i pojemnika, w którym można ją przechowywać.Druga kwestia polega na tym, że chociaż relacje między kontenerami, z których można odczytać różne typy obiektów, zachowują się tak samo, jak relacje między samymi obiektami, te między kontenerami, w których można umieszczać różne typy obiektów, zachowują się przeciwnie do swoich treści . Każda klatka, o której wiadomo, że zawiera instancję,
Cat
będzie klatką, która zawiera instancjęAnimal
, ale nie musi być klatką, która zawiera instancjęSiameseCat
. Z drugiej strony, każda klatka, która może pomieścić wszystkie instancje,Cat
będzie klatką, która może pomieścić wszystkie instancjeSiameseCat
, ale nie musi być klatką, która może pomieścić wszystkie instancjeAnimal
. Jedyny rodzaj klatki, który może pomieścić wszystkie wystąpieniaCat
i może być zagwarantowany, nigdy nie pomieści niczego innego niż przypadekCat
, jest klatkąCat
. Każdy inny rodzaj klatki albo nie byłby w stanie zaakceptować niektórych przypadków,Cat
które powinien zaakceptować, albo byłby w stanie zaakceptować rzeczy, które nie są przypadkamiCat
.źródło