2009-07-13 22 views
12

Say Mam klasy, który wygląda tak:Zasada pojedynczej odpowiedzialności: czy wszystkie metody publiczne w klasie muszą używać wszystkich zależności klasowych?

internal class SomeClass 
{ 
    IDependency _someDependency; 

    ... 


    internal string SomeFunctionality_MakesUseofIDependency() 
    { 
    ... 
    } 
} 

A potem chcę dodać funkcjonalność, która jest związana ale sprawia wykorzystywania innego uzależnienia, aby osiągnąć swój cel. Być może coś jak poniżej:

internal class SomeClass 
{ 
    IDependency _someDependency; 

    IDependency2 _someDependency2; 

    ... 


    internal string SomeFunctionality_MakesUseofIDependency() 
    { 
    ... 
    } 

    internal string OtherFunctionality_MakesUseOfIDependency2() 
    { 
    ... 
    } 
} 

Kiedy pisać testy jednostkowe dla tej nowej funkcjonalności (lub zaktualizować testy jednostkowe, które mam do istniejącej funkcjonalności), znajdę się tworząc nową instancję SomeClass (w SUT) podczas przekazywania wartości null dla zależności, której nie potrzebuję dla określonego fragmentu funkcjonalności, który zamierzam przetestować.

To wydaje mi się nieprzyjemnym zapachem, ale powodem, dla którego znalazłem się na tej ścieżce, jest to, że stworzyłem nowe zajęcia dla każdej nowej funkcjonalności, którą wprowadziłem. To też wydawało się złe, więc zacząłem próbować grupować podobne funkcje.

Moje pytanie: czy wszystkie zależności danej klasy powinny być zużywane przez całą jej funkcjonalność, np. Jeśli różne bity funkcji używają różnych zależności, jest to wskazówka, że ​​powinny one prawdopodobnie żyć w osobnych klasach?

Odpowiedz

3

Does SomeClass utrzymać stan wewnętrzny, czy jest to po prostu „montażu” różne elementy funkcjonalności? Można przepisać to w ten sposób:

internal class SomeClass 
{ 
    ... 


    internal string SomeFunctionality(IDependency _someDependency) 
    { 
    ... 
    } 

    internal string OtherFunctionality(IDependency2 _someDependency2) 
    { 
    ... 
    } 
} 

W tym przypadku, nie może złamać SRP jeśli SomeFunctionality i OtherFunctionality są jakoś (funkcjonalnie) związane, które nie wynika z użyciem zastępczych.

I masz dodatkową wartość, jaką jest możliwość wyboru zależności od klienta, a nie czasu utworzenia/DI. Może niektóre testy definiujące przypadki użycia dla tych metod pomogłyby wyjaśnić sytuację: Jeśli możesz napisać sensowny przypadek testowy, w którym obie metody są wywoływane na tym samym obiekcie, to nie zrywasz SRP.

Jeśli chodzi o wzór Fasady, widziałem, że zbyt wiele razy szaleje, aby to polubić, wiesz, kiedy skończysz z 50+ metodami ... Pytanie brzmi: Dlaczego tego potrzebujesz? Ze względów związanych z wydajnością a la old-timer EJB?

+0

Rozwiązuje to mój problem, ponieważ zależności nie mają pojęcia o stanie, ale raczej są enkapsulującymi funkcjonalnościami (powinienem wspomnieć o tym w moim pytaniu - ale nie rozważał tego w tym czasie) – jpoh

0

zwykle metody do poszczególnych kategorii, jeśli wykorzystują wspólny kawałek stanie, który może być zamknięty w klasie. Posiadanie zależności, które nie są używane przez wszystkie metody w klasie, może być zapachem kodu, ale nie bardzo silnym. Zwykle dzielę się tylko metodami z klas, gdy klasa staje się zbyt duża, klasa ma zbyt wiele zależności lub metody nie mają wspólnego stanu.

0

Moje pytanie: czy wszystkie zależności od klasy być spożywane przez wszystkich jego funkcjonalności to znaczy jeśli różne bity funkcjonalności stosować różne zależności, jest to wskazówka, że ​​powinny one prawdopodobnie mieszkają w oddzielnych klasach?

Jest to wskazówka wskazująca, że ​​twoja klasa może być trochę niespójna ("robienie czegoś więcej niż tylko jednej rzeczy"), ale jak mówisz, jeśli podejmiesz to za daleko, skończysz z nową klasą za każdą nową funkcjonalność. Więc chcesz wprowadzić obiekty elewacyjne, aby je ponownie połączyć (wydaje się, że obiekt fasady jest dokładnie przeciwny tej konkretnej zasadzie projektowania).

Musisz znaleźć dobrą równowagę, która pracuje dla Ciebie (i resztę zespołu).

+0

Chciałbym usłyszeć, jeśli ktoś tam uważa, że ​​fasada jest przeciwieństwem tej reguły. – Calanus

0

Wygląda na przeciążenia do mnie. Próbujesz coś zrobić i są dwa sposoby, aby to zrobić, w taki czy inny sposób. Na poziomie SomeClass będę miał jedną zależność do wykonania pracy, a następnie ta jedna zależna klasa obsługuje dwa (lub więcej) sposobów na zrobienie tego samego, najprawdopodobniej z wzajemnie wykluczającymi się parametrami wejściowymi. Innymi słowy, będę miał ten sam kod, który masz na SomeClass, ale zdefiniuj go jako SomeWork zamiast nie zawierać żadnego innego niepowiązanego kodu.

HTH

0

Elewacja jest używana, gdy chcesz ukryć złożoność (jak interfejs do starszego systemu) lub chcesz skonsolidować funkcjonalność, będąc kompatybilnym wstecz z perspektywy interfejsu.

Kluczem w twoim przypadku jest to, dlaczego masz dwie różne metody w tej samej klasie. Czy intencją jest mieć klasę, która grupuje podobne typy zachowań, nawet jeśli jest zaimplementowana za pomocą niepowiązanego kodu, tak jak w agregacji. Lub, czy próbujesz wspierać to samo zachowanie, ale masz alternatywne implementacje w zależności od specyfiki, co byłoby wskazówką dla rozwiązania typu dziedziczenia/przeciążania.

Problem będzie polegał na tym, czy ta klasa będzie nadal rosnąć iw jakim kierunku. Dwie metody nie będą miały znaczenia, ale jeśli powtórzy się więcej niż 3, musisz zdecydować, czy chcesz zadeklarować je jako elewację/adapter, czy też musisz utworzyć klasy potomne dla odmian.

Twoje podejrzenia są poprawne, ale zapach jest tylko smużką dymu z płonącego żaru. Musisz go mieć na oku, na wypadek gdyby wybuchł, a następnie musisz podjąć decyzję, jak chcesz ugasić pożar, zanim wypali się spod kontroli.

11

Gdy każda metoda instancji dotyka każdej zmiennej instancji, wówczas klasa jest maksymalnie spójna. Gdy żadna metoda instancji nie współdzieli zmiennej instancji z żadną inną, klasa jest minimalnie spójna. Choć prawdą jest, że lubimy spójność, to prawda, że ​​obowiązuje zasada 80-20. Uzyskanie tego ostatniego niewielkiego zwiększenia spójności może wymagać dużego wysiłku.

Ogólnie rzecz biorąc, jeśli masz metody, które nie używają niektórych zmiennych, jest to zapach. Ale niewielki zapach nie wystarczy, aby całkowicie odmienić klasę. Należy się tym przejmować i mieć oko, ale nie zalecam natychmiastowego działania.