Wstrzykiwanie zależności a metody statyczne

21

Przeprowadziłem dzisiaj interesującą dyskusję z innym programistą na temat tego, jak podejść do klasy za pomocą metody, która akceptuje ciąg i ciąg wyjściowy.

Wyobraź sobie coś takiego, co zostało w całości wykonane na potrzeby przykładu

public string GetStringPart(string input)
{ 
   //Some input validation which is removed for clarity

   if(input.Length > 5)
        return input.Substring(0,1);

   if(input.Substring(0,1) == "B")
        return input.Substring(0,3);

   return string.empty;
}

Funkcja, która ma pewną logikę na podstawie danych wejściowych ciągu, jest dodawana do projektu za pomocą DI i ma kontener DI na swoim miejscu. Czy dodasz tę nową klasę z interfejsem i wstrzykniesz ją tam, gdzie to konieczne, czy uczynisz ją klasą statyczną? Jakie są zalety i wady każdego z nich? Dlaczego miałbyś (lub nie) chcieć zrobić to z zastrzykiem konstruktora, a nie tylko uzyskiwać do niego dostęp w razie potrzeby w dowolnym miejscu.

James
źródło
1
@Ewan Dla metod pomocniczych, które nie mają zastosowania do abstrakcji. Przykład: Apache FileUtils.
Walfrat
3
@Ewan: metody statyczne bez skutków ubocznych są najlepszymi metodami, ponieważ są łatwe do zrozumienia i przetestowania.
JacquesB
1
ale uniemożliwiają jednostkowe testowanie rzeczy, które od nich zależą
Ewan
3
@Ewan Eh, niezupełnie. Czy Math.Max ​​() jest zły, ponieważ jest statyczny? Jeśli przetestujesz swoją metodę statyczną i działa ona, możesz bezpiecznie używać jej na innych metodach bez problemów. Jeśli statyczny zawiedzie, jego test go złapie.
T. Sar - Przywróć Monikę
1
jeśli „statyczny” gwarantowałby brak skutków ubocznych, mógłbym zobaczyć argument. ale tak nie jest.
Ewan

Odpowiedzi:

25

Nie ma powodu, aby to robić. To tylko funkcja, nie ma zależności, więc po prostu ją wywołaj. Może być nawet statyczny, jeśli chcesz, ponieważ wygląda na czysty. Bez trudu można napisać testy jednostkowe. Jeśli jest używany w innych klasach, testy jednostkowe mogą być nadal pisane.

Nie ma potrzeby abstrakcji funkcji bez zależności, to przesada.

Jeśli stanie się to bardziej złożone, być może uzasadnione jest przekazanie interfejsu do konstruktora lub metody. Ale nie poszedłbym tą drogą, chyba że miałbym złożoną GetStringPartlogikę opartą na lokalizacji itp.

Jon Raynor
źródło
2
To poprawna odpowiedź! Niestety, kultowa odpowiedź została zaakceptowana.
JacquesB
3
nie chodzi o to, że funkcja nie ma żadnych zależności. problem polega na tym, że inne rzeczy zależą od funkcji i ściśle się z nią
wiążą
2
Co jeśli funkcja zmieni się w ciągu 6 miesięcy i nie będzie tak czysta, ale jest już używana w wielu miejscach. Nie jest rozszerzalny jako statyczny i nie jest też czymś, co można odizolować od testów jednostkowych klas konsumpcyjnych. Jeśli istnieje choć niewielka szansa na zmianę, wybrałbym ją na zastrzyk. To powiedziawszy, jestem otwarty na słuchanie sprzecznych argumentów, o to właśnie chodzi w tym poście. Jakie są wady wstrzykiwania i zalety elektryczności statycznej?
James
1
Ogólnie rzecz biorąc, ludzie omawiający zastrzyk zależności na tej stronie nie potrzebują zastrzyku zależności. Stąd tendencja do niepotrzebnej złożoności.
Frank Hileman,
2
@James Jeśli funkcja staje się mniej czysta, robisz coś złego, a funkcja prawdopodobnie nie była dobrze zdefiniowana od samego początku. Obliczenie powierzchni kwadratu nie powinno nagle wymagać wywołania bazy danych ani wdrożenia aktualizacji wizualnej dla interfejsu użytkownika. Czy możesz sobie wyobrazić scenariusz, w którym rodzaj metody „podciągania” nagle staje się nieczysty?
T. Sar - Przywróć Monikę
12

Dlatego

class DOSClient {
    OrderParser orderParser;
    string orderCode;

    DOSClient(OrderParser orderParser, string ordercode) { 
        this.orderParser = orderParser; 
        this.ordercode = ordercode;
    }
    void DisplayOrderCode() {
        Console.Write( "Prefix: " + orderParser.GetStringPart(ordercode) ); 
        ...
    }
}

class GUIClient {
    OrderParser orderParser;
    string orderCode;
    GUI gui;

    GUIClient(OrderParser orderParser, string ordercode, GUI gui) { 
        this.orderParser = orderParser; 
        this.ordercode = ordercode;
        this.gui = gui;
    }

    void DisplayOrderCode() {
        gui.Prefix( orderParser.GetStringPart(ordercode) ); 
        ...
    }
}

 

class OrderParserUS : IOrderParser {

    public string GetStringPart(string input)
    { 
        //Some input validation which is removed for clarity

        if(input.Length > 5)
            return input.Substring(0,1);

        if(input.Substring(0,1) == "B")
            return input.Substring(0,3);

        return string.empty;
    }
}

class OrderParserEU : IOrderParser {

    public string GetStringPart(string input)
    { 
        //Some input validation which is removed for clarity

        if(input.Length > 6)
            return input.Substring(0,1);

        if(input.Substring(0,1) == "#")
            return input.Substring(0,3);

        return string.empty;
    }
}

Jeśli wybrałeś metodę statyczną, nie byłoby sposobu, aby zmienić zachowanie GetStringPartbez zniszczenia starego zachowania lub zanieczyszczenia go logiką warunkową. To prawda, że ​​statyka to złe globale w przebraniu, ale fakt, że wyłączają polimorfizm, jest moim głównym zarzutem. Metody statyczne nie są pierwszą klasą w językach OOP. Dając metodzie obiekt do życia, nawet taki bez stanu, sprawiamy, że metoda jest przenośna. Jego zachowanie można przekazywać jak wartość zmiennej.

Wyobraziłem sobie system, który musi zachowywać się nieco inaczej po wdrożeniu w Europie niż po wdrożeniu w USA. Zamiast tego wymuszaj, aby jeden system zawierał kod potrzebny tylko drugiemu, możemy zmienić zachowanie, kontrolując, jaki obiekt analizujący kolejność jest wprowadzany do klientów. To pozwala nam ograniczyć rozprzestrzenianie się szczegółów regionu. Ułatwia także dodawanie OrderParserCanada bez konieczności dotykania istniejących parserów.

Jeśli to nic dla ciebie nie znaczy, to naprawdę nie ma na to dobrego argumentu.

BTW, GetStringPartto okropne imię.

candied_orange
źródło
* Cringe w stylu kodu w nawiasach * Zgaduję, że jesteś programistą Java próbującym napisać kod C #? Przypuszczam, że trzeba jednego. :)
Neil,
Pytanie nie jest specyficzne dla języka, bardziej dotyczy projektowania. Mój problem z metodami statycznymi polega na tym, że uniemożliwiają one izolację kodu do testowania. Deweloper, z którym rozmawiałem, uważa, że ​​zbyt mocno polegam na DI, a czasem metody rozszerzenia / metody statyczne są istotne. Myślę, że w rzadkich przypadkach, ale nie jako cel ogólny. Trwająca dyskusja, którą czułem, była dobra do dalszej dyskusji. Zgadzam się również z twoim argumentem za polimorfizmem.
James
Testowanie jest powodem, ale jest ich więcej. Nie lubię obwiniać tego zbytnio o testowanie, ponieważ to dopiero zaczyna krytykować testy. Prostym faktem jest, że programiści proceduralni nie lubią tego, jeśli nie programujesz proceduralnie.
candied_orange
Nie możesz po prostu powiedzieć static getStringPartEU()? Twój przykład ma sens tylko wtedy, gdy istnieją inne metody w tej klasie, które również wymagają specjalistycznego leczenia w UE i muszą być traktowane jako jedna jednostka.
Robert Harvey
2
Zdecydowanie opowiadasz się za niepotrzebną złożonością. Do wszystkiego można dodawać coraz więcej kodu. Rzeczy, które należy zmienić, powinny być łatwe do modyfikacji, ale nie powinieneś próbować przewidywać, co powinno się zmienić w przyszłości. Pola statyczne mogą być identyczne jak zmienne globalne, ale metody statyczne mogą być czyste, najlepszy możliwy rodzaj metody.
Frank Hileman,