Hej miraski. Mam problem projektowy i zastanawiam się jak go rozwiązać, żeby nie pogwałcić zasady Open-Closed Principle.
Przypuśćmy, że mamy klasę abstrakcyjną
Shape . Mamy też kilka klas konkretnych, tj.
Circle ,
Triangle , wszystkie dziedziczą po
Shape i implementują metodę
draw . Do tej pory wszystko jest zajebiście, OCP jest zachowana, super. Potrzebuję jednak metody, powiedzmy o takiej sygnaturze:
Shape getProperShape(Whatever someArgument) która na podstawie odpowiedniej wartości argumentu zwróci mi obiekt odpowiedniej klasy dziedziczącej po Shape. A więc przykładowo jeśli dostanie argument o takiej wartości to zwróci mi
Circle , jak o innej to
Triangle itd.
Teraz tak, dodanie takiej metody statycznej do klasy
Shape już będzie pogwałceniem tej reguły - bo jak dojdzie nowy kształt, powiedzmy
Square , to ta metoda będzie wymagać zmiany, aby obsługiwała tę nową klasę.
Jak to rozwiązać? Stworzyć osobną klasę np.
ShapeFactory ? Ale to w sumie dalej będzie gwałcenie tej zasady, tylko że na innej klasie.
@Marmite: Tak czy tak gdzieś musisz mieć logikę "mapującą" wartości argumentu someArgument na konkretne klasy. Normalnie robi się to wzorcem metody fabrycznej (FactoryMethod, bardzo podobne do tego co sam wymyśliłeś z metodą statyczną), ale jak sam zauważyłeś nie jest to idealne rozwiązanie, bo również łamie OCP
@korri: (#) Jeśli dobrze mi się wydaje, to chyba takie rozwiązanie jak dał @MacDada: (#) pozwala nie łamać OCP no chyba że źle patrzę. Ale generalnie to chyba jest kompromis, albo OCP, albo klasa typu Factory, prawda?
@Marmite: Tak. To jest kompromis. Wynika to z tego, że ta logika mapowania o której wyżej pisałem, gdzieś fizycznie musi "leżeć". Jeśli leży w jednej oddzielnej klasie, to mamy klasyczne Factory - jeśli natomiast jest "rozsmarowana" po innych klasach typu providery/bezpośredno shape'y - to mamy rozwiązanie zaproponowane przez @MacDada i zachowane OCP.
@Marmite: Przykładowo tak działa (#php) Symfony Serializer.
Zanim dane/obiekty zostaną zserializowane np do JSONa, muszą zostać „spłaszczone” („znormalizowane”) do prostych danych. Każdy normalizator implementuje NormalizerInterface z dwiema
@Hauleth: (#) Z tego co widzę to właściwie sama ta funkcja jest złamaniem zasady Liskov - szkoda, że nigdzie nie ma informacji czym ją w dobry sposób zastąpić :< nawet Wujek Bob tutaj wspomina o analogicznej funkcji, ale potem już rozważa trochę inne rzeczy...
Mega udany debiut, nie mogłem zrobić nic lepiej, nie mogłem być tego dnia wyżej, sprawiedliwie oceniony. Życiowa forma, waga 6kg większa niż 1,5 roku temu, czyli 4kg czystej masy mięśniowej dobudowane rocznie uśredniając.
Przypuśćmy, że mamy klasę abstrakcyjną
Shape. Mamy też kilka klas konkretnych, tj.
Circle,
Triangle, wszystkie dziedziczą po
Shapei implementują metodę
draw. Do tej pory wszystko jest zajebiście, OCP jest zachowana, super. Potrzebuję jednak metody, powiedzmy o takiej sygnaturze:
Shape getProperShape(Whatever someArgument)która na podstawie odpowiedniej wartości argumentu zwróci mi obiekt odpowiedniej klasy dziedziczącej po Shape. A więc przykładowo jeśli dostanie argument o takiej wartości to zwróci mi
Circle, jak o innej to
Triangleitd.
Teraz tak, dodanie takiej metody statycznej do klasy
Shapejuż będzie pogwałceniem tej reguły - bo jak dojdzie nowy kształt, powiedzmy
Square, to ta metoda będzie wymagać zmiany, aby obsługiwała tę nową klasę.
Jak to rozwiązać? Stworzyć osobną klasę np.
ShapeFactory? Ale to w sumie dalej będzie gwałcenie tej zasady, tylko że na innej klasie.
#programowanie #cleancode #oop
Komentarz usunięty przez autora Wpisu
Shape, powiedzmy tenże
@Marmite: Tak, druga opcja to nie sprawdzać czy null, tylko dać dwie metody:
Zanim dane/obiekty zostaną zserializowane np do JSONa, muszą zostać „spłaszczone” („znormalizowane”) do prostych danych. Każdy normalizator implementuje NormalizerInterface z dwiema