Nie używaj „statycznego” w C #?

109

Złożyłem aplikację, którą napisałem do innych architektów w celu przejrzenia kodu. Jeden z nich niemal natychmiast odpisał mi i powiedział: „Nie używaj„ statycznego ”. Nie możesz pisać automatycznych testów za pomocą klas i metod statycznych. Należy unikać„ statycznego ”.

Sprawdziłem iw pełni 1/4 moich zajęć jest oznaczonych jako „statyczne”. Używam static, gdy nie zamierzam tworzyć instancji klasy, ponieważ klasa jest pojedynczą klasą globalną używaną w całym kodzie.

Później wspomniał o kpinie, technikach IOC / DI, których nie można używać ze statycznym kodem. Mówi, że niefortunnie jest, gdy biblioteki stron trzecich są statyczne z powodu ich braku możliwości testowania.

Czy ten drugi architekt ma rację?

aktualizacja: oto przykład:

APIManager - ta klasa przechowuje słowniki interfejsów API innych firm, do których dzwonię, wraz z następnym dozwolonym czasem. Egzekwuje limity użytkowania interfejsu API, które wiele podmiotów zewnętrznych ma w swoich warunkach świadczenia usług. Używam go wszędzie, gdzie dzwonię do usługi innej firmy, dzwoniąc do Thread.Sleep (APIManager.GetWait („ProviderXYZ”)); przed wykonaniem połączenia. Wszystko tutaj jest wątkowo bezpieczne i działa świetnie z TPL w C #.

Infin8Loop
źródło
37
staticjest w porządku; static pola należy traktować bardzo ostrożnie
Marc Gravell
2
Dlaczego miałby pisać testy dla bibliotek stron trzecich? Czy zwykle nie testujesz własnego kodu, zakładając, że twórcy biblioteki innej firmy wykonali testy?
Nie,
3
Ten szczególny zarzut jest dla mnie trochę zbyt ogólny. W większości przypadków nie mocowałbyś klasy statycznej, wyśmiewałbyś argumenty. Pole statyczne, w którym była to klasa zagregowana, która wymagała kpin, a następnie tak.
8
Najwyraźniej twoi koledzy mają poważny stan - ostry ogólnoustrojowy rumień OOPus. Potrzebują leczenia JAK NAJSZYBCIEJ!
SK-logic
6
Istnieje sekcja odpowiedzi na udzielanie odpowiedzi, nie rozumiem, dlaczego ludzie komentują, próbując dać komuś odpowiedź / sugestię ... To przeczy celowi StackExchange, z pewnością komentarzem byłoby żądanie dodatkowych informacji.
peteski

Odpowiedzi:

121

Zależy to od tego, czy klasy statyczne utrzymują stan, czy nie. Osobiście nie mam problemu z funkcjami bezstanowymi zrzuconymi razem w klasie statycznej.

Larsenal
źródło
111
Poprawny. Funkcje statyczne, które nie mają skutków ubocznych, są najbardziej testowalnymi funkcjami ze wszystkich.
Robert Harvey,
9
+1 do tego, ale z klauzulą ​​bezpieczeństwa: prawie każdy abstrakcyjny algorytm jest bezstanowy, ale to nie znaczy, że powinieneś wdrożyć go jako bezstanową klasę statyczną lub metodę. Zaimplementowanie go w ten sposób powoduje, że cały kod, który go używa, nie jest z nim powiązany. Oznacza to na przykład, że jeśli zaimplementujesz algorytm sortowania jako metodę statyczną i użyjesz go w projekcie - że nie będziesz mógł tymczasowo zastąpić sortowania innym algorytmem, tj. Do celów testowych i zawężenia obszaru problemowego. W wielu przypadkach jest to ogromna przeszkoda. Poza tym, statyczny jest całkowicie OK, jeśli jest stosowany rozsądnie.
11
Krótko mówiąc: są to funkcje najbardziej testowalne, ale niekoniecznie sprawiają, że drugi kod jest bardziej testowalny! To prawdopodobnie miał na myśli architekt.
4
@ tereško: Język C # wymaga, aby metody statyczne były częścią klasy statycznej, jeśli nie chcesz tworzyć instancji klasy w celu wywołania metody. Być może masz na myśli „instancję”, a nie „klasę”.
Robert Harvey
8
@quetzalcoatl: Przekazanie delegata (tj. innego algorytmu) metodzie statycznej jest całkowicie legalne; wszystkie metody rozszerzenia w Linq są metodami statycznymi. Przykład: msdn.microsoft.com/en-us/library/…
Robert Harvey
93

Jest w tym zbyt ogólny. Ma rację, utrudnia to testowanie. Jednak staticklasy i metody mają swoje miejsce i tak naprawdę zależy od kontekstu. Bez przykładów kodu nie można tak naprawdę powiedzieć.

Używam statycznego, gdy nie zamierzam tworzyć instancji klasy, ponieważ klasa jest pojedynczą klasą globalną używaną w całym kodzie.

Może to być silny zapach kodu. Do czego używasz klas? Do przechowywania danych? Aby uzyskać dostęp do bazy danych? Więc on ma rację. W takim przypadku powinieneś zajrzeć do wstrzykiwania zależności, ponieważ taka klasa statyczna jest w rzeczywistości niejawnym singletonem.

Jeśli używasz ich do metod rozszerzenia lub pomocników, które nie zmieniają stanu i działają tylko na podanych przez Ciebie parametrach, zwykle są one w porządku.

Femaref
źródło
3
+1 za wzmiankę o tym, że oświadczenie było ogólne i o metodach rozszerzenia, które również można przetestować, a zależności, o ile mi wiadomo, można jeszcze wyśmiewać.
Nie,
4
Tak statyczne jak instancja pojedynczej klasy jako niejawny singleton, będzie bałagan ...
„Aby uzyskać dostęp do bazy danych?” Dlaczego nie ? jeśli adapter tabeli nie jest statyczny, to utworzenie zestawu danych @ o silnym typie danych, nic złego z nim .... chyba że udowodnisz swój punkt odniesienia lub przykładu?
Matematyka
27

Sprawdziłem iw pełni 1/4 moich zajęć jest oznaczonych jako „statyczne”. Używam statycznego, gdy nie zamierzam tworzyć instancji klasy, ponieważ klasa jest pojedynczą klasą globalną używaną w całym kodzie.

Najlepiej jest przetestować kod jednostkowo. Spróbuj zaprojektować testy, które są powtarzalne, niezależne, proste i przetestuj tylko jedną metodę na raz. Spróbuj uruchomić testy w innej losowej kolejności. Czy możesz uzyskać stabilną „zieloną” wersję?

Jeśli tak, to ważny punkt do obrony twojego kodu. Jeśli jednak masz trudności, być może powinieneś wybrać metody oparte na instancjach.

oleksii
źródło
7
To właśnie mnie uderzyło. Kilka statycznych i bezpaństwowych klas „pomocniczych” jest w porządku, ale jeśli masz pełne 25% wszystkich swoich klas jako statycznych, jest mało prawdopodobne, aby twoje wysiłki były ograniczone do tego przypadku.
Matthew Scharley,
2
@MatthewScharley - prawdopodobnie dostał 4 klasy, a 1 z nich jest statyczny :)
Alexus
1
Uważaj, że jeśli użyjesz takich elementów (takich jak singletony), późniejsze tworzenie kolejnych instancji może być trudnym zadaniem. Podobnie jak np. Tworzenie aplikacji obsługującej dokument, napotykasz problemy później, gdy chcesz zarządzać wieloma dokumentami.
Michel Keijzers,
11

Odpowiedzi już opublikowane obejmują wiele naprawdę dobrych punktów, ale wydaje się, że brakuje jednego:

Pola statyczne nigdy nie są usuwane.

Jest to bardzo ważne, jeśli masz aplikację z wieloma ograniczeniami pamięci, a ten wzór może być bardzo częsty, gdy ludzie próbują zaimplementować pamięć podręczną.

Funkcje statyczne nie są już tak złe, ale wszyscy inni opisali to już wystarczająco szczegółowo.

riwalk
źródło
10

Jedną z zalet, które można uzyskać z IoC / DI, jest to, że większość interakcji między klasami jest negocjowana między interfejsami. Ułatwia to testowanie jednostek, ponieważ interfejsy można wyśmiewać automatycznie lub półautomatycznie, a zatem każdą z części można przetestować pod kątem wejść i wyjść. Co więcej, poza testabilty, umieszczenie interfejsów między wszystkim pozwala uzyskać najbardziej modułowy kod - możesz łatwo zastąpić jedną implementację bez większego obawy, że spieprzysz zależności.

Ponieważ C # nie ma metaklasy, klasa nie może zaimplementować interfejsu przy użyciu funkcji statycznych, więc statyczne cechy klas kończą wszelkie próby implementacji czystego modelu obiektowego IoC / DI. Innymi słowy, nie możesz stworzyć makiety, aby je przetestować i musisz stworzyć prawdziwe zależności.

Jeśli twoja firma / projekt jest mocno zainwestowany w robienie IoC, jest to oczywiście uzasadniony problem, a ból, przez który przechodzisz, jest dla korzyści wszystkich. Jednak z architektonicznego punktu widzenia osobiście nie sądzę, aby jakikolwiek model poszedł do grobu. Jest kilka rzeczy, które mają sens za pomocą metod statycznych - na przykład strategia projektowania singletonów. Sam jestem mniej skłonny do klas statycznych, ponieważ myślę, że zaczynają tracić zalety OO, ale są chwile, kiedy klasa biblioteczna jest prawdopodobnie bardziej naturalnym wyrażeniem czegoś niż silnika.


Komentator przypomina mi metody rozszerzeń, które oczywiście muszą znajdować się w klasach statycznych w języku C #. Jest to uzasadnione i jest przykładem tego, jak czysty IoC nie działa zbyt dobrze w języku C #, przynajmniej jeśli próbujesz skorzystać z szerokiej gamy języka.

jwrush
źródło
1
Jeśli klasa jest statyczna z właściwych powodów i poprawnie zaimplementowana, na przykład metody rozszerzenia, nadal można ją testować, ponieważ nie miałyby żadnych zewnętrznych zależności i byłyby niezależne i jako takie nie powinny wymagać kpiny. Czy potrzeba interfejsu nie powinna wynikać z wymogu projektowego, aby jak najlepiej wypełnić wymaganie biznesowe, a nie ze względu na utrzymanie czystego projektu IoC / DI?
Nie,
1
Ty i ja zgadzamy się co do ogólnej kwestii, że powinieneś wybrać odpowiednie narzędzie do właściwej pracy, a nie być kajdanami filozofii. Jeśli jednak w twoim projekcie czystego IoC / DI istnieje dobrze ugruntowana kultura, będzie to bolało tak samo, jak konwersja tradycyjnie odgórnego rozwiązania na IoC / DI. Inżynieria musi wziąć pod uwagę koszty przejściowe. Nawiasem mówiąc, nie sądzę, że metody rozszerzenia całkiem cię tam prowadzą. Myślę, że lepszym rozwiązaniem dla zachowania IoCish z klasami statycznymi byłoby wybranie wielu klas pomiędzy w czasie kompilacji z dyrektywami preprocesora, styl C
jwrush,
1
Er. Jestem głupi. Miałeś na myśli klasę statyczną dla metod rozszerzenia HOLDING, co oczywiście wymaga ich wykonania. Tak, oczywiście, w tym celu potrzebujesz klasy statycznej. To wymknęło mi się z głowy: i to pokazuje, że C # nie był przeznaczony dla IoC. :)
jwrush,
Tak, masz rację, jeśli istniejąca kultura w istniejącym projekcie byłaby bardziej szkodliwa niż pomoc w zmianie sposobu, w jaki się to robi.
Nie,
5

Klasy statyczne są czasami niewłaściwie wykorzystywane i powinny być:

  • singleton (gdzie klasa niestatyczna ma element statyczny i publiczną zmienną statyczną Instancji, aby pobrać / utworzyć ją tylko raz.
  • metoda z innej klasy (w której liczy się stan). Jeśli masz członka, który nie korzysta z danych z tej klasy, prawdopodobnie nie powinien on należeć do tej klasy.

Może to być również tak zwana funkcja „użyteczności” i część klasy użyteczności. Klasy narzędzi to klasy, które zawierają tylko metody statyczne i służą jako funkcje pomocnicze bez żadnego kontekstu.

Michel Keijzers
źródło
@topomorto Ponieważ klasa nie powinna być tworzona przez jej konstruktor, co powoduje powstanie wielu obiektów / instancji, ale za pomocą specjalnej metody (zwykle instance ()) <, która zwraca zawsze tę samą instancję, tworzona po pierwszym wywołaniu instancji ().
Michel Keijzers,
@topomorto Właśnie sprawdziłem i masz całkowitą rację, tylko niektórzy członkowie są statyczni (zmienna instancji i funkcja instancji). Odpowiednio zmienię swoją odpowiedź; dzięki za wzmiankę.
Michel Keijzers,
4

Metody statyczne są odpowiednie w użyciu i mają należne miejsce w programowaniu. Wygląda na to, że Twój architekt ma środowisko testowe, które nie w pełni obsługuje metody statyczne. Jeśli Twój kod jest częścią większego projektu, ważne jest, aby spełnić wytyczne architektów. Nie pozwól jednak, aby ten projekt zniechęcił cię do używania metod statycznych, gdy jest to właściwe.

k rey
źródło
1

Używam właściwości statycznych do rzeczy, które są wspólne dla wszystkich instancji klasy. I używam metod statycznych, aby uzyskać grupy obiektów klasy. Nie jestem bynajmniej ekspertem, ale do tej pory działało na mnie.

Przykład PHP:

class vendorData {
  private static $data_table = 'vendor_data'; // static property
  private $id; // instance property
  private $name; // instance property

  public function __construct($vendor_id) {
    if(!self::validId($vendor_id) ) { // static method
      return false; // id doesn't exist
    }
    $this->id = $vendor_id; // id has been validated
    $this->getProperties(); // object method
  }

  private static function validId($vendor_id) {
    // send db query to find if the id exists
    // return true/false;
  }

  private function getProperties() { // object method
    $sql = "SELECT * FROM `{self::$data_table}` // using static property
        WHERE `id` = {$this->id}"; // using object instance property
    // get resultset
    foreach($result as $property => $value) {
      $this->$property = $value; // object instance properties all set
    }
  }

  // and here
  public static function getBy($property,$value) { // static method to return object array
    $sql = "SELECT `id` FROM `{self::$data_table}` // using static property
      WHERE `$property` = '$value'";
    // send query, get $ids array
    $obj_array = array();
    foreach($ids as $id) {
      // create array of current class objects using special static keyword
      // meaning of static here is different than in property/method declarations
      $obj_array[$id] = new static($id);
    }
    return $obj_array;
  }
}
Buttle Butkus
źródło
1

Powiem, że ich zasady są w porządku, ale stwierdzenie (nie używaj statyki) może być błędne. Ile kosztuje Twój kod? jeśli liczba jest wysoka i czujesz się komfortowo z testowaniem jednostkowym swojej aplikacji, to nic ci nie jest. Jeśli nie, proponuję przejrzeć kod. Może się zdarzyć, że przyczyną są klasy statyczne lub nie, będzie to zależeć od wielu rzeczy.


źródło
-3

Zajęcia metodami statycznymi naruszają zasady OOP. To już nie jest OOP, to programowanie zorientowane na klasę COP.

Rzadko mają wyraźną „osobowość” (używam tutaj mojej ulubionej ludzkiej metafory ) lub tożsamość. Więc nie wiedzą, kim są. Sam ten punkt wystarczy, aby pozbyć się tej koncepcji w OOP, ale jest ich więcej.

Następny jest konsekwencją pierwszego punktu. Ponieważ takie klasy nie wiedzą, kim są, zwykle są duże lub duże.

Po trzecie, twój kod jest absolutnie nie do utrzymania. Zastosowanie metod statycznych wprowadza ukryte zależności . Pojawiają się wszędzie i nigdy nie wiesz, co dzieje się w twojej klasie. Nie możesz być tego pewien, patrząc tylko na podpis konstruktora.

Powoli, ale nieuchronnie, twój kod staje się coraz mniej spójny, ponieważ ukryte zależności są słabo kontrolowane. Wydają się niewidoczne. I tak łatwo jest dodać kolejny! Nie musisz modyfikować podpisu żadnej metody. Wszystko, co musisz zrobić, to wywołać metodę statyczną. A co za brzydki kod wynika ...

Ok, pewnie już zgadłeś. Szczelne połączenie często towarzyszy niskiej kohezji. Jeśli jest wielu klientów korzystających z jakiejś klasy, łączenie staje się coraz ściślejsze. I jest duża uwodzenie w użyciu już napisanej klasy lub metody, która wymaga tylko drobnej poprawki. Tylko jeden parametr-parametr metody.

Czego używać zamiast metod statycznych? Cóż, moja propozycja to OOP. Dlaczego nie spróbować?

Zapadło
źródło