Czytam Obiekty PHP, Wzorce i Praktykę . Autor próbuje modelować lekcję na uczelni. Celem jest przedstawienie rodzaju lekcji (wykładu lub seminarium) oraz opłat za lekcję w zależności od tego, czy jest to lekcja godzinowa, czy stała. Tak więc wynik powinien być
Lesson charge 20. Charge type: hourly rate. Lesson type: seminar.
Lesson charge 30. Charge type: fixed rate. Lesson type: lecture.
gdy dane wejściowe są następujące:
$lessons[] = new Lesson('hourly rate', 4, 'seminar');
$lessons[] = new Lesson('fixed rate', null, 'lecture');
Ja to napisałem:
class Lesson {
private $chargeType;
private $duration;
private $lessonType;
public function __construct($chargeType, $duration, $lessonType) {
$this->chargeType = $chargeType;
$this->duration = $duration;
$this->lessonType = $lessonType;
}
public function getChargeType() {
return $this->getChargeType;
}
public function getLessonType() {
return $this->getLessonType;
}
public function cost() {
if($this->chargeType == 'fixed rate') {
return "30";
} else {
return $this->duration * 5;
}
}
}
$lessons[] = new Lesson('hourly rate', 4, 'seminar');
$lessons[] = new Lesson('fixed rate', null, 'lecture');
foreach($lessons as $lesson) {
print "Lesson charge {$lesson->cost()}.";
print " Charge type: {$lesson->getChargeType()}.";
print " Lesson type: {$lesson->getLessonType()}.";
print "<br />";
}
Ale zgodnie z książką się mylę (jestem pewien, że też). Zamiast tego autor podał dużą hierarchię klas jako rozwiązanie. W poprzednim rozdziale autor podał następujące „cztery drogowskazy” jako czas, w którym powinienem rozważyć zmianę struktury mojej klasy:
- Duplikacja kodu
- Klasa, która za dużo wiedziała o jej kontekście
- Gniazdem wszystkich branż - zajęcia, które starają się robić wiele rzeczy
- Instrukcje warunkowe
Jedyny problem, jaki widzę, to stwierdzenia warunkowe, i to również w niejasny sposób - więc po co to refaktoryzować? Jak myślisz, jakie problemy mogą pojawić się w przyszłości, których nie przewidziałem?
Aktualizacja : zapomniałem wspomnieć - jest to struktura klas, którą autor przedstawił jako rozwiązanie - wzorzec strategii :
źródło
Odpowiedzi:
To zabawne, że książka nie podaje tego jasno, ale powodem, dla którego faworyzuje hierarchię klas, jeśli instrukcje w tej samej klasie jest prawdopodobnie zasada Otwarta / Zamknięta Ta powszechnie znana reguła projektowania oprogramowania stanowi, że klasa powinna być zamknięta dla modyfikacja, ale otwarta do rozszerzenia.
W twoim przykładzie dodanie nowego typu lekcji oznaczałoby zmianę kodu źródłowego klasy Lekcji, czyniąc ją delikatną i podatną na regresję. Jeśli miałeś klasę podstawową i jedną pochodną dla każdego rodzaju lekcji, zamiast tego musiałbyś po prostu dodać kolejną podklasę, która jest ogólnie uważana za czystszą.
Możesz umieścić tę zasadę w sekcji „Oświadczenia warunkowe”, jeśli chcesz, jednak uważam, że „drogowskaz” jest nieco niejasny. Jeśli stwierdzenia są na ogół tylko zapachem kodu, objawy. Mogą wynikać z wielu złych decyzji projektowych.
źródło
Myślę, że celem tej książki jest unikanie takich rzeczy jak:
Moja interpretacja książki jest taka, że powinieneś mieć trzy klasy:
Następnie należy ponownie zaimplementować
cost
funkcję w obu podklasach.Moja osobista opinia
dla takich prostych przypadków: nie. To dziwne, że twoja książka preferuje głębszą hierarchię klas, aby uniknąć prostych instrukcji warunkowych. Co więcej, przy specyfikacji wejściowej
Lesson
będzie musiała być fabryka z wysyłką, która jest instrukcją warunkową. Dzięki rozwiązaniu książkowemu będziesz miał:To większa złożoność bez dodatkowych korzyści.
źródło
SemesterLesson
,MasterOneWeekLesson
itp abstrakcyjna klasa korzeń, albo jeszcze lepiej interfejs, jest zdecydowanie do zrobienia potem. Ale kiedy masz tylko dwie sprawy, autor może według własnego uznania.Przykład w książce jest dość dziwny. Z twojego pytania oryginalne oświadczenie brzmi:
co w kontekście rozdziału o OOP oznaczałoby, że autor prawdopodobnie chce mieć następującą strukturę:
Ale potem pojawia się cytowany przez Ciebie wkład:
tzn. coś, co nazywa się kodem typu ciągłego i które, szczerze mówiąc, w tym kontekście, gdy czytelnik ma się uczyć OOP, jest okropne.
Oznacza to również, że jeśli mam rację co do intencji autora książki (tj. Tworzenia tych klas abstrakcyjnych i dziedziczonych, które wymieniłem powyżej), doprowadzi to do zduplikowania i dość nieczytelnego i brzydkiego kodu:
Jeśli chodzi o „drogowskazy”:
Powielanie kodu
Twój kod nie ma duplikatu. Kod, którego autor oczekuje od ciebie, pisze.
Klasa, która za dużo wiedziała o swoim kontekście
Twoja klasa po prostu przekazuje ciągi od wejścia do wyjścia i nic nie wie. Z drugiej strony, oczekiwany kod może być krytykowany za zbyt dużą wiedzę.
The Jack of All Trades - Klasy, które próbują robić wiele rzeczy
Ponownie, tylko przekazujecie sznurki, nic więcej.
Instrukcje warunkowe
W twoim kodzie jest jedna instrukcja warunkowa. Jest czytelny i łatwy do zrozumienia.
Być może autor spodziewał się, że napiszesz kod o wiele bardziej przebudowany niż ten z sześcioma klasami powyżej. Na przykład możesz użyć wzoru fabrycznego do lekcji i opłat itp.
W takim przypadku skończysz z dziewięcioma klasami, które będą doskonałym przykładem nadmiaru architektury .
Zamiast tego otrzymałeś łatwy do zrozumienia, czysty kod, który jest dziesięć razy krótszy.
źródło
Przede wszystkim możesz zmienić „magiczne liczby”, takie jak 30 i 5 w funkcji cost (), na bardziej znaczące zmienne :)
I szczerze mówiąc, uważam, że nie powinieneś się tym martwić. Kiedy będziesz musiał zmienić klasę, zmienisz ją. Najpierw spróbuj stworzyć wartość, a następnie przejdź do refaktoryzacji.
A dlaczego myślisz, że się mylisz? Czy ta klasa nie jest dla ciebie „wystarczająco dobra”? Dlaczego martwisz się o jakąś książkę;)
źródło
Absolutnie nie ma potrzeby implementowania żadnych dodatkowych klas. Masz trochę
MAGIC_NUMBER
problemu w swojejcost()
funkcji, ale to wszystko. Wszystko inne jest nadmierną inżynierią. Niestety często zdarza się, że udzielane są bardzo złe rady - na przykład Circle dziedziczy Kształt. Zdecydowanie nie jest efektywne w żaden sposób wyprowadzanie klasy dla prostego dziedziczenia jednej funkcji. Zamiast tego możesz zastosować funkcjonalne podejście, aby je dostosować.źródło