Załóżmy, że mamy moduł oprogramowania A, który implementuje funkcję F. Inny moduł B implementuje tę samą funkcję co F '.
Istnieje wiele sposobów na pozbycie się duplikatu kodu:
- Niech A użyje F 'z B.
- Niech B użyje F z A.
- Umieść F we własnym module C i pozwól, aby korzystały z niego zarówno A, jak i B.
Wszystkie te opcje generują dodatkowe zależności między modułami. Stosują zasadę OSUSZANIA kosztem rosnącego sprzężenia.
O ile widzę, sprzężenie jest zawsze zwiększane lub w leasingu przenoszone na wyższy poziom podczas stosowania DRY. Wydaje się, że istnieje konflikt między dwiema najbardziej podstawowymi zasadami projektowania oprogramowania.
(Właściwie nie wydaje mi się zaskakujące, że takie konflikty są takie. Prawdopodobnie to sprawia, że dobry projekt oprogramowania jest tak trudny. Zaskakujące jest to, że konflikty te zwykle nie są omawiane w tekstach wprowadzających.)
Edycja (dla wyjaśnienia): Zakładam, że równość F i F 'to nie tylko przypadek. Jeśli F będzie musiał zostać zmodyfikowany, F 'prawdopodobnie będzie musiał zostać zmodyfikowany w ten sam sposób.
źródło
Odpowiedzi:
Dlaczego tak. Ale zmniejszają sprzężenie między liniami. Otrzymujesz moc zmiany sprzęgła. Sprzężenie występuje w wielu formach. Wyodrębnianie kodu zwiększa pośrednie i abstrakcyjne. Zwiększenie, które może być dobre lub złe. Najważniejszą rzeczą, która decyduje o tym, którą masz, jest nazwa, której używasz. Jeśli patrzenie na to nazwisko mnie zaskakuje, kiedy zajrzę do środka, to nikomu nie wyświadczyłeś przysług.
Ponadto, nie podążaj za OSUSZANIEM w próżni. Jeśli zabijesz duplikat, bierzesz odpowiedzialność za przewidywanie, że te dwa zastosowania tego kodu zmienią się razem. Jeśli prawdopodobnie zmienią się one niezależnie, spowodowałeś zamieszanie i dodatkową pracę przynoszącą niewielkie korzyści. Ale naprawdę dobre imię może sprawić, że będzie to smaczniejsze. Jeśli wszystko, co możesz wymyślić, to złe imię, proszę, po prostu przestań.
Łączenie zawsze będzie istnieć, chyba że system jest tak odizolowany, że nikt nigdy nie będzie wiedział, czy działa. Sprzężenie refaktoryzacyjne to gra polegająca na wyborze trucizny. Postępowanie w trybie DRY może się opłacić, minimalizując sprzężenie powstałe w wyniku wielokrotnego wyrażania tej samej decyzji projektowej w wielu miejscach, dopóki zmiana nie będzie bardzo trudna. Ale DRY może uniemożliwić zrozumienie twojego kodu. Najlepszym sposobem na uratowanie tej sytuacji jest znalezienie naprawdę dobrego imienia. Jeśli nie potrafisz wymyślić dobrego imienia, mam nadzieję, że potrafisz unikać imion bez znaczenia
źródło
Istnieją sposoby na przełamanie jawnych zależności. Popularnym jest wstrzykiwanie zależności w czasie wykonywania. W ten sposób uzyskuje się OSUSZANIE, zdejmowanie sprzęgła kosztem bezpieczeństwa statycznego. Obecnie jest tak popularny, że ludzie nawet nie rozumieją, że jest to kompromis. Na przykład kontenery aplikacji rutynowo zapewniają zarządzanie zależnościami, co znacznie komplikuje oprogramowanie, ukrywając złożoność. Nawet zwykły zastrzyk starego konstruktora nie gwarantuje niektórych kontraktów z powodu braku systemu typów.
Aby odpowiedzieć na tytuł - tak, jest to możliwe, ale bądź przygotowany na konsekwencje wysyłki środowiska uruchomieniowego.
W ten sposób jedynym typem zależności, jakie można mieć, jest D w zależności od każdego modułu.
Lub zarejestruj C w kontenerze aplikacji z wbudowanym wstrzykiwaniem zależności i ciesz się fortunami autowired wolno rosnących pętli i zakleszczeń klas środowiska wykonawczego.
źródło
Nie jestem pewien, czy odpowiedź bez dalszego kontekstu ma sens.
Czy
A
już zależyB
lub odwrotnie? - w takim przypadku możemy mieć oczywisty wybór domuF
.Czy
A
iB
już udostępniać żadnych wspólnych zależności, które mogą być dobre do domuF
?Jak duży / złożony jest
F
? Od czego jeszczeF
zależy?Moduły są
A
iB
stosowane w tym samym projekcie?Czy
A
i takB
ostatecznie skończy się wspólna zależność?Jaki system językowy / modułowy jest używany: Jak bolesny jest nowy moduł, w bólu programisty, w nakładzie wydajności? Na przykład, jeśli piszesz w C / C ++ z systemem modułowym COM, co powoduje ból w kodzie źródłowym, wymaga alternatywnego oprzyrządowania, ma wpływ na debugowanie i ma wpływ na wydajność (w przypadku wywołań między modułami), mógłbym zrób poważną przerwę.
Z drugiej strony, jeśli mówisz o bibliotekach Java lub C # DLL, które łączą się dość płynnie w środowisku pojedynczego wykonania, to inna sprawa.
Funkcja jest abstrakcją i obsługuje DRY.
Jednak dobre abstrakty muszą być kompletne - niekompletne abstrakty mogą bardzo dobrze powodować, że konsumujący klient (programista) uzupełnia niedobór, wykorzystując wiedzę o podstawowej implementacji: skutkuje to ściślejszym sprzężeniem, niż gdyby abstrakcja była oferowana jako pełniejsza.
Tak więc, argumentowałbym za stworzeniem lepszej abstrakcji
A
iB
polegania na niej niż po prostu przeniesieniem jednej funkcji do nowego modułuC
.Szukałbym zestawu funkcji, które dałyby początek nowej abstrakcji, co oznacza, że mógłbym poczekać, aż baza kodu będzie dalej, aby zidentyfikować pełniejsze / bardziej kompletne refaktoryzowanie abstrakcji zamiast jednego opartego na jeden kod funkcji powiedzieć.
źródło
Does A already depend on B or vice versa? — in which case we might have an obvious choice of home for F.
Zakłada się, że A zawsze będzie polegać na B (lub odwrotnie), co jest bardzo niebezpiecznym założeniem. Fakt, że OP postrzega F jako nieodłączną część A (lub B) sugeruje, że F istnieje bez bycia nieodłącznym dla żadnej biblioteki. Jeśli F należy do jednej biblioteki (np. Metody rozszerzenia DbContext (F) i biblioteki opakowującej Entity Framework (A lub B)), pytanie OP byłoby dyskusyjne.Odpowiedzi, które koncentrują się na wszystkich sposobach „zminimalizowania” tego problemu, wyrządzają ci krzywdę. A „rozwiązania” po prostu oferujące różne sposoby tworzenia sprzężenia wcale nie są naprawdę rozwiązaniami.
Prawda jest taka, że nie rozumieją samego problemu, który stworzyłeś. Problem z twoim przykładem nie ma nic wspólnego z DRY, a raczej (szerzej) z projektowaniem aplikacji.
Zadaj sobie pytanie, dlaczego moduły A i B są oddzielne, jeśli oba zależą od tej samej funkcji F? Oczywiście będziesz mieć problemy z zarządzaniem zależnościami / abstrakcją / sprzężeniem / you-name-it, jeśli popełnisz zły projekt.
Właściwe modelowanie aplikacji odbywa się zgodnie z zachowaniem. Jako takie, części A i B, które są zależne od F, należy wyodrębnić do ich własnego, niezależnego modułu. Jeśli nie jest to możliwe, należy połączyć A i B. W obu przypadkach A i B nie są już użyteczne dla systemu i powinny przestać istnieć.
DRY to zasada, której można użyć do ujawnienia złego projektu, a nie do jego spowodowania. Jeśli nie możesz osiągnąć OSUSZANIA ( gdy naprawdę ma to zastosowanie - zwracając uwagę na edycję) ze względu na strukturę aplikacji, jest to wyraźny znak, że struktura stała się zobowiązaniem. Dlatego „ciągłe refaktoryzowanie” jest również zasadą, której należy przestrzegać.
ABC innych zleceniodawców projektowych (mocne, suche, itp) tam są wszystkie używane do zmieniania (włączając refactoring) wniosek bardziej bezbolesne. Skup się na tym , a wszystkie inne problemy zaczną znikać.
źródło
Mam inną opinię, przynajmniej w przypadku trzeciej opcji:
Z twojego opisu:
Umieszczenie F w module C nie zwiększa sprzężenia, ponieważ zarówno A, jak i B już potrzebują funkcji C.
źródło