2012-08-01 3 views
5

Niedawno przeczytałem o dyrektorze inwersji zależności w doskonałej książce Roberta.C.Martina Zwinne zasady, wzorce i praktyki w języku C#. Jest jednak jeden aspekt tego zlecenia, który moim zdaniem nie w pełni rozumiem.W jaki sposób funkcja Dependency-Inversion Principal (DIP) zmniejsza potrzebę wprowadzania zmian w modułach zależnych, gdy zmieniają się zależności?

Robert wyjaśnia, że ​​gdy moduły wysokopoziomowe są zależne od modułów niższego poziomu, zmiany w modułach niższego poziomu mogą również spowodować zmianę modułów wyższego poziomu. Demonstruje to z poniższym przykładzie:

public class Button 
{ 
    private Lamp lamp; 
    public void Poll(){ 
     if(/*some condition*/) 
     Lamp.TurnOn(); 
    } 
} 

O tym kodzie Robert mówi „Klasa Przycisk zależy bezpośrednio od klasy Lamp Zależność ta oznacza, że ​​przycisk będzie wpływ zmian do źródła światła.”

Jak widzę to możliwe są dwa rodzaje zmian, które możemy wprowadzić do klasy lampy:

1) może chcemy zmienić wewnętrzną implementację klasy, ale bez wpływu na interfejs publiczny.

2) Możemy zdecydować się na zmianę interfejsu publicznego, aby przekazać parametr do metody TurnOn.

Nie rozumiem, że w pierwszym przypadku, dlaczego nasze zmiany spowodują zmianę w klasie Button? Publiczny interfejs do lampy nie uległ zmianie, więc dlaczego przycisk musiałby się zmienić?

W drugim przypadku widzę, że wymagałoby to zmiany przycisku. Ale w takim przypadku, w jaki sposób w zależności od abstrakcji może to zmienić? Z pewnością, jeśli mam uzasadniony powód, aby zmienić interfejs na Lampę, to zmieniałbym także interfejs w abstrakcji, od której zależy Lamp i Button. W takim przypadku muszę zmienić przycisk tak, jak zmieniła się abstrakcja, od której to zależy.

Zdaję sobie sprawę, że istnieją inne korzyści dla DIP, takie jak możliwość ponownego użycia modułów wyższego poziomu, posiadanie interfejsów przez moduły wyższego poziomu oraz możliwość wyboru implementacji zależności w czasie wykonywania, jednak staram się zrozumieć, w jaki sposób zmniejsza się DIP potrzeba zmiennych modułów do zmiany, gdy zmienia się interfejs do modułu niższego poziomu i/lub dlaczego wewnętrzne zmiany w module zależnym mogą powodować zmiany w modułach wyższego poziomu.

Odpowiedz

1

Uważam, że istotną różnicą, którą DIP wprowadza w tym przykładzie, jest własność interfejsu. Szczególnie w której warstwie znajduje się interfejs, gdzie Button jest klientem, a lampa jest serwerem.

W zależności od konkretnej klasy Lampa interfejs (.TurnOn()) jest własnością klasy Lamp (serwer). W związku z tym można podjąć decyzję o zmianie metody .TurnOn() opartej wyłącznie na potrzebach serwera, ponieważ jest on właścicielem metody, co wymaga późniejszej zmiany w klasie Button (kliencie).

Po pobraniu interfejsu do klasy interfejsu ISwitchableDevice/Abstract własność jest przenoszona do klienta lub warstwy współdzielonej. Dlatego zmiana interfejsu nie może być sterowana bezpośrednio przez potrzeby serwerów, wszelkie zmiany w klasie Lamp (posiadanej przez serwer) mogą być dokonywane bez zmiany interfejsu. A jeśli wymagane są zmiany w interfejsie ISwitchableDevice, będzie to zależało od potrzeb klienta lub warstwy współdzielonej.

0

Na przykład, jeśli chodzi o interfejs, jeśli wprowadzono zmiany w konstruktorze lampy (która jest częścią publicznego interfejsu) i jesteś zależny od abstrakcyjnej bazy lub interfejsu, zmiany te nie byłyby propagowane do implementacja przycisku (chyba, że ​​go tam skonstruujesz, ale jest to częściowo inny problem).
Jeśli chodzi o "czystą implementację", prawidłowo obudowany projekt, w którym nie wprowadzono żadnych zmian w interfejsie (konstruktorzy, wyjątki, które można wyrzucać itp.) I nie zmieniły się wspólne globalne zależności, nie powinien mieć wpływu na dzwoniącego, więc to nie jest problem.

Zasadniczo, w odniesieniu do tych kwestii, redukujemy powierzchnię, więc jesteśmy zależni od mniejszej szczegółowości, chociaż pewne szczegóły będą zawsze konieczne.

Bez względu na to, czy abstrakcja jest bezużytecznym bałaganem, czy nieocenionym rozdziałem, należy to rozstrzygnąć od początku do końca.

+0

Dzięki Daniel. Widzę, że zmiana konstruktora konkretnej usługi jest przykładem, gdzie zależność od interfejsu izolowałaby klasę zależną od zmiany (zakładając, że nie wykonuje ona konstrukcji), ale nadal czuję, że czegoś mi brakuje. Robert mówi o uzależnieniu przechodnim w swoim artykule: jeśli A-> B i B-> C, to zmiana w klasie C może wymusić zmianę w klasie B iw klasie A_. Wciąż nie bardzo rozumiem, jak to się dzieje. Nawet jeśli wprowadziliśmy zmianę w publicznym interfejsie C, dlaczego miałoby to wpływ na A? – John

+0

jeśli klasa c get jest nową zależnością, a ta zależność nie jest dostępna w klasie b, ale jest dostępna w a, to może wymagać tej zależności od b, więc b może ją dostarczyć do c – Daniel

Powiązane problemy