To pytanie jest subiektywne, ale byłem ciekawy, jak podchodzi do tego większość programistów. Poniższy przykład jest w pseudo-C #, ale powinno to również dotyczyć języków Java, C ++ i innych języków OOP.
W każdym razie, pisząc metody pomocnicze w moich klasach, mam tendencję do deklarowania ich jako statycznych i po prostu przekazuję pola, jeśli metoda pomocnicza ich potrzebuje. Na przykład, biorąc pod uwagę poniższy kod, wolę używać metody wywołania nr 2 .
class Foo
{
Bar _bar;
public void DoSomethingWithBar()
{
// Method Call #1.
DoSomethingWithBarImpl();
// Method Call #2.
DoSomethingWithBarImpl(_bar);
}
private void DoSomethingWithBarImpl()
{
_bar.DoSomething();
}
private static void DoSomethingWithBarImpl(Bar bar)
{
bar.DoSomething();
}
}
Moim powodem jest to, że wyjaśnia (przynajmniej moim oczom), że metoda pomocnicza ma możliwy efekt uboczny na inne obiekty - nawet bez czytania jej implementacji. Uważam, że mogę szybko sprawdzać metody wykorzystujące tę praktykę, a tym samym pomagać mi w debugowaniu rzeczy.
Co wolisz robić we własnym kodzie i jakie są tego powody?
Odpowiedzi:
To naprawdę zależy. Jeśli wartości, na których operują pomocnicy, są prymitywne, dobrym wyborem są metody statyczne, jak zauważył Péter.
Jeśli są one skomplikowane, a następnie STAŁY dotyczy, bardziej szczegółowo z S , z I i D .
Przykład:
To dotyczy twojego problemu. Państwo może uczynić
makeEveryBodyAsHappyAsPossible
metodę statyczną, która będzie miała w niezbędnych parametrów. Inną opcją jest:Teraz
OurHouse
nie musisz wiedzieć o zawiłościach zasad dystrybucji plików cookie. Musi tylko teraz obiekt, który implementuje regułę. Implementacja jest abstrahowana od obiektu, którego wyłączną odpowiedzialnością jest stosowanie reguły. Ten obiekt można przetestować w izolacji.OurHouse
można przetestować za pomocą zwykłej makietyCookieDistributor
. Możesz łatwo zmienić zasady dystrybucji plików cookie.Uważaj jednak, aby tego nie przesadzić. Na przykład posiadanie złożonego systemu 30 klas działa jako implementacja
CookieDistributor
, w której każda klasa po prostu spełnia małe zadanie, naprawdę nie ma sensu. Moja interpretacja SRP jest taka, że nie tylko dyktuje, że każda klasa może mieć tylko jedną odpowiedzialność, ale także, że jedna klasa powinna być wypełniona przez jedną klasę.W przypadku prymitywów lub obiektów, których używasz jak prymitywów (na przykład obiektów reprezentujących punkty w przestrzeni, macierzy lub czegoś takiego), statyczne klasy pomocnicze mają wiele sensu. Jeśli masz wybór, a to naprawdę ma sens, możesz rozważyć dodanie metody do klasy reprezentującej dane, np. Rozsądne jest,
Point
aby miećadd
metodę. Ponownie, nie przesadzaj.Zatem w zależności od problemu istnieją różne sposoby rozwiązania tego problemu.
źródło
Powszechnie znanym idiomem jest deklarowanie metod klas użyteczności
static
, więc takich klas nigdy nie trzeba tworzyć. Przestrzeganie tego idiomu ułatwia zrozumienie kodu, co jest dobrą rzeczą.Istnieje jednak poważne ograniczenie tego podejścia: takich metod / klas nie można łatwo wyśmiewać (chociaż AFAIK przynajmniej dla C # istnieją frameworki, które potrafią to osiągnąć, ale nie są one powszechne i przynajmniej niektóre z nich są Reklama w telewizji). Więc jeśli metoda pomocnicza ma jakąkolwiek zewnętrzną zależność (np. DB), która sprawia, że - a więc jej wywołujący - jest trudny do testowania jednostkowego, lepiej jest zadeklarować ją jako niestatyczną . Umożliwia to wstrzykiwanie zależności, dzięki czemu osoby wywołujące metodę są łatwiejsze do testowania jednostkowego.
Aktualizacja
Wyjaśnienie: powyższe mówi o klasach użyteczności, które zawierają tylko metody pomocnicze niskiego poziomu i (zazwyczaj) nie ma stanu. Inną kwestią są metody pomocnicze w stanowych klasach nieprzydatnych do użycia; przeprosiny za błędną interpretację PO.
W tym przypadku wyczuwam zapach kodu: metoda klasy A, która działa przede wszystkim na instancji klasy B, może faktycznie mieć lepsze miejsce w klasie B. Ale jeśli zdecyduję się zachować go tam, gdzie jest, wolę opcję 1, ponieważ jest prostszy i łatwiejszy do odczytania.
źródło
Wolę # 1 (
this->DoSomethingWithBarImpl();
), chyba że metoda pomocnicza nie potrzebuje dostępu do danych / implementacji instancjistatic t_image OpenDefaultImage()
.interfejs publiczny i prywatna implementacja oraz członkowie są osobni. programy byłyby o wiele trudniejsze do odczytania, gdybym wybrał # 2. z numerem 1 zawsze mam członków i instancję dostępne. w porównaniu do wielu implementacji opartych na OO, zwykle używam dużej ilości enkapsualacji i bardzo niewielu członków na klasę. jeśli chodzi o skutki uboczne - nie mam nic przeciwko, często istnieje walidacja stanu, która rozszerza zależności (np. argumenty, które należałoby przekazać).
# 1 jest znacznie prostszy w czytaniu, utrzymaniu i ma dobre cechy wydajnościowe. oczywiście będą przypadki, w których można udostępnić implementację:
Gdyby # 2 było dobrym wspólnym rozwiązaniem (np. Domyślnym) w bazie kodu, martwiłbym się strukturą projektu: czy klasy robią za dużo? czy nie ma wystarczającej ilości kapsułkowania lub ponownego użycia? czy poziom abstrakcji jest wystarczająco wysoki?
wreszcie # 2 wydaje się zbędny, jeśli używasz języków OO, w których obsługiwana jest mutacja (np.
const
metoda).źródło