Prawdopodobnie tak jak wielu, często napotykam bóle głowy z problemami projektowymi, w których na przykład istnieje pewien wzór / podejście projektowe, które wydaje się intuicyjnie pasować do problemu i przynosi pożądane korzyści. Bardzo często występuje pewne zastrzeżenie, które utrudnia wdrożenie wzorca / podejścia bez jakiejś pracy, która następnie neguje korzyści wynikające z wzorca / podejścia. Mogę bardzo łatwo skończyć z wieloma wzorcami / podejściami, ponieważ przewidywalnie prawie wszystkie mają pewne bardzo istotne zastrzeżenia w rzeczywistych sytuacjach, w których po prostu nie jest łatwe rozwiązanie.
Przykład:
Dam ci hipotetyczny przykład oparty luźno na prawdziwym, z którym się ostatnio spotkałem. Powiedzmy, że chcę używać kompozycji zamiast dziedziczenia, ponieważ hierarchie dziedziczenia utrudniały skalowalność kodu w przeszłości. Mogę zmienić kod, ale stwierdzę, że istnieją pewne konteksty, w których nadklasa / klasa podstawowa po prostu musi wywołać funkcjonalność podklasy, pomimo prób jej uniknięcia.
Kolejne najlepsze podejście wydaje się polegać na wdrożeniu wzoru połowy delegata / obserwatora i wzoru połowy składu, aby nadklasa mogła przekazać zachowanie lub aby podklasa mogła obserwować zdarzenia nadklasy. Wtedy klasa jest mniej skalowalna i łatwa w utrzymaniu, ponieważ niejasne jest, w jaki sposób należy ją rozszerzyć, a także trudno jest rozszerzyć istniejących słuchaczy / delegatów. Również informacje nie są dobrze ukryte, ponieważ trzeba znać implementację, aby zobaczyć, jak rozszerzyć nadklasę (chyba że bardzo intensywnie używasz komentarzy).
Więc po tym mogę zdecydować się po prostu całkowicie wykorzystać obserwatorów lub delegatów, aby uniknąć wad związanych z intensywnym mieszaniem podejść do. Jednak wiąże się to z własnymi problemami. Na przykład może się okazać, że potrzebuję obserwatorów lub delegatów do rosnącej liczby zachowań, dopóki nie będę potrzebować obserwatorów / delegatów do praktycznie każdego zachowania. Jedną z opcji może być jeden wielki odbiornik / delegat dla wszystkich zachowań, ale wtedy klasa implementująca kończy się wieloma pustymi metodami itp.
Potem mógłbym spróbować innego podejścia, ale jest z tym tyle samo problemów. Potem następny, następny po itd.
Ten iteracyjny proces staje się bardzo trudny, gdy każde podejście wydaje się mieć tyle problemów, co inne, i prowadzi do pewnego rodzaju paraliżu decyzji projektowych . Trudno jest również zaakceptować fakt, że kod stanie się równie problematyczny, niezależnie od zastosowanego wzorca projektowego lub podejścia. Jeśli skończę w takiej sytuacji, czy to oznacza, że sam problem wymaga ponownego przemyślenia? Co robią inni, gdy napotykają taką sytuację?
Edycja: Wydaje się, że istnieje kilka interpretacji pytania, które chcę wyjaśnić:
- Całkowicie usunąłem OOP z pytania, ponieważ okazuje się, że tak naprawdę nie jest ono specyficzne dla OOP, a ponadto zbyt łatwo jest źle zinterpretować niektóre komentarze, które przekazałem na temat OOP.
- Niektórzy twierdzą, że powinienem zastosować podejście iteracyjne i wypróbować różne wzorce lub odrzucić wzór, gdy przestanie on działać. Jest to proces, do którego przede wszystkim chciałem się odnieść. Pomyślałem, że to wynika z przykładu, ale mogłem to wyjaśnić, więc zredagowałem to pytanie.