Dlaczego i kiedy powinienem uczynić klasę „statyczną”? Jaki jest cel „statycznego” słowa kluczowego w klasach?

47

Słowo statickluczowe członka w wielu językach oznacza, że ​​nie powinieneś tworzyć instancji tej klasy, aby mieć dostęp do tego członka. Nie widzę jednak żadnego uzasadnienia, aby stworzyć całą klasę static. Dlaczego i kiedy powinienem zrobić zajęcia static?

Jakie korzyści czerpię z tworzenia zajęć static? Mam na myśli, że po zadeklarowaniu klasy statycznej nadal należy zadeklarować wszystkich członków, do których chce mieć dostęp bez instancji, jako statyczne.

Oznacza to, że na przykład Mathklasa może zostać zadeklarowana jako normalna (nie statyczna), bez wpływu na sposób programowania programistów. Innymi słowy, uczynienie klasy statyczną lub normalną jest dla programistów trochę przejrzyste.

Saeed Neamati
źródło
Jeśli kodujesz w stylu Func Prog a la Rich Hickey, może to być przydatne.
Job
1
Moim zdaniem klasy statyczne są jedynie kulą, przez którą C # próbuje ukryć ogromną lukę projektową w języku. Po co ustrukturyzować język wokół tej bezsensownej ideologii „wszystko to musi być klasą”, aby wprowadzić dodatkową składnię, aby oszukiwać i omijać to niepotrzebne ograniczenie. Gdyby język C # zezwalał na bezpłatne funkcje od samego początku, nie byłoby potrzeby stosowania klas statycznych.
antred

Odpowiedzi:

32

Ułatwia użytkownikom zrozumienie sposobu wykorzystania klasy. Na przykład kompletnym nonsensem byłoby napisanie następującego kodu:

Math m = new Math();

C # nie musi tego zabraniać, ale ponieważ nie służy to żadnemu celowi, równie dobrze może powiedzieć o tym użytkownikowi. Niektóre osoby (w tym ja) wyznają filozofię, zgodnie z którą języki programowania (i interfejsy API…) powinny być tak restrykcyjne, jak to tylko możliwe, aby uczynić je trudnymi w użyciu: jedynymi dozwolonymi operacjami są wtedy te, które są znaczące i (mam nadzieję) prawidłowe.

Konrad Rudolph
źródło
3
Nie zgadzam się z tą filozofią. Mam problem, który wymaga zmiany. Tak więc, chociaż może być sensowne, aby go ograniczyć, to ograniczenie utrudnia nasz rozwój na przyszłe potrzeby. A starsza biblioteka, której jesteśmy zmuszeni używać do interfejsu ze starym systemem (tym, który napisałeś, zapieczętowałeś i zredagowałeś wszystkie klasy) nie zapewnia nam możliwości rozszerzenia lub modyfikacji tego kodu, który napisałeś. To, że nie miałeś powodu, aby tworzyć swoje zajęcia, nie oznacza, że ​​nie będę tego potrzebować w przyszłości. Podczas gdy statystyka klasy użyteczności ma sens, rozważ także przyszłość.
SoylentGray
19
@Chad Biblioteka jest źle zaprojektowana. Rozszerzanie klas, które nie zostały zaprojektowane do rozszerzenia, ostatecznie nie działa, więc najlepiej stwórz klasę sealedna pierwszym miejscu. Przyznaję, że nie wszyscy ludzie zgadzają się z tym sentymentem, ale komentarze nie są dobrym miejscem do dyskusji na ten temat. Ale w przypadku statictego problemu nawet się nie stawia: tworzenie instancji nigdy nieSystem.Math będzie miało sensu.
Konrad Rudolph
1
@Konard, jeśli zadeklarujesz wszystkich członków Mathklasy jako statyczną bez deklarowania Mathklasy jako statyczną, nadal możesz jej używać bez potrzeby tworzenia instancji.
Saeed Neamati
11
@ Saeed - To prawda, ale możesz również utworzyć instancję klasy, co nie służy żadnemu celowi. Co mam zrobić z instancją klasy Math? Aby wyraźnie pokazać zamiar klasy (nie jest wymagana ani pożądana instancja), jest zaznaczony static.
Corbin,
3
@ Saeed Odwróćmy to: proponuję, aby ponownie przeczytać moją odpowiedź, ponieważ wydaje się, że źle ją zrozumiałeś: Twoje komentarze nie mają związku z moją odpowiedzią. Nigdy nie mówiłem, że musisz pisać new Math().
Konrad Rudolph
24

StackOverflow ma świetną dyskusję na ten temat . Dla ułatwienia skopiuję go i wkleję tutaj, w imieniu jego autora, Mark S. Rasmussen :

Moje przemyślenia na temat klas statycznych zapisałem we wcześniejszym wątku :

Uwielbiałem klasy użytkowe wypełnione metodami statycznymi. Dokonali wielkiej konsolidacji metod pomocniczych, które w innym przypadku leżałyby wokół powodując piekło nadmiarowości i utrzymania. Są bardzo łatwe w użyciu, bez tworzenia instancji, bez usuwania, po prostu odpal i zapomnij. Wydaje mi się, że była to moja pierwsza nieświadoma próba stworzenia architektury zorientowanej na usługi - wiele bezpaństwowych usług, które wykonały swoją pracę i nic więcej. Jednak wraz z rozwojem systemu nadchodzą smoki.

Wielopostaciowość

Powiedzmy, że mamy metodę UtilityClass.SomeMethod, która szczęśliwie się rozwija. Nagle musimy nieco zmienić funkcjonalność. Większość funkcji jest taka sama, ale musimy jednak zmienić kilka części. Gdyby nie była to metoda statyczna, moglibyśmy stworzyć klasę pochodną i w razie potrzeby zmienić zawartość metody. Ponieważ jest to metoda statyczna, nie możemy. Jasne, jeśli musimy tylko dodać funkcjonalność przed lub po starej metodzie, możemy utworzyć nową klasę i wywołać w niej starą klasę - ale to po prostu obrzydliwe.

Interfejs nieszczęścia

Z powodów logicznych nie można definiować metod statycznych za pomocą interfejsów. A ponieważ nie możemy przesłonić metod statycznych, klasy statyczne są bezużyteczne, gdy musimy przekazać je za pomocą interfejsu. To uniemożliwia nam użycie klas statycznych jako części wzorca strategii. Możemy rozwiązać niektóre problemy, przekazując delegatów zamiast interfejsów.

Testowanie

Zasadniczo idzie to w parze z problemami interfejsu wspomnianymi powyżej. Ponieważ nasza zdolność do wymiany implementacji jest bardzo ograniczona, będziemy mieli problemy z zastąpieniem kodu produkcyjnego kodem testowym. Ponownie możemy je zawinąć, ale będzie to wymagało od nas zmiany dużych części naszego kodu, abyśmy mogli akceptować opakowania zamiast rzeczywistych obiektów.

Przyspiesza plamy

Ponieważ metody statyczne są zwykle używane jako metody narzędziowe, a metody narzędziowe zwykle mają różne cele, szybko otrzymamy dużą klasę wypełnioną niespójną funkcjonalnością - idealnie, każda klasa powinna mieć jeden cel w systemie. Wolę pięć razy więcej zajęć, o ile ich cele są dobrze określone.

Pełzanie parametrów

Na początek ta słodka i niewinna metoda statyczna może wymagać jednego parametru. W miarę wzrostu funkcjonalności dodaje się kilka nowych parametrów. Wkrótce dodawane są dodatkowe parametry, które są opcjonalne, dlatego tworzymy przeciążenia metody (lub po prostu dodajemy wartości domyślne w językach, które je obsługują). Wkrótce mamy metodę, która przyjmuje 10 parametrów. Tylko pierwsze trzy są naprawdę wymagane, parametry 4-7 są opcjonalne. Ale jeśli parametr 6 jest określony, należy również wypełnić 7-9 ... Gdybyśmy stworzyli klasę, której jedynym celem jest zrobienie tego, co zrobiła ta metoda statyczna, moglibyśmy rozwiązać ten problem, przyjmując wymagane parametry w konstruktor i umożliwiając użytkownikowi ustawianie wartości opcjonalnych za pomocą właściwości lub metod ustawiania wielu współzależnych wartości w tym samym czasie. Również,

Wymaganie od konsumentów utworzenia instancji klas bez powodu

Jednym z najczęstszych argumentów jest: dlaczego żądać od konsumentów z naszej klasy utworzenia instancji w celu wywołania tej pojedynczej metody, nie mając przy tym później żadnej instancji? Utworzenie instancji klasy jest bardzo tanią operacją w większości języków, więc szybkość nie stanowi problemu. Dodanie do wiersza dodatkowego wiersza kodu jest niskim kosztem, jeśli chodzi o położenie podwalin pod znacznie łatwiejsze w utrzymaniu rozwiązanie w przyszłości. I wreszcie, jeśli chcesz uniknąć tworzenia instancji, po prostu stwórz opakowanie singletonowe swojej klasy, które pozwala na łatwe ponowne użycie - chociaż powoduje to, że twoja klasa jest bezstanowa. Jeśli nie jest to bezstanowy, nadal możesz tworzyć statyczne metody otoki, które obsługują wszystko, a jednocześnie dają Ci wszystkie korzyści na dłuższą metę. Wreszcie,

Tylko Sith zajmuje się absolutami

Oczywiście są wyjątki od mojej niechęci do metod statycznych. Prawdziwe klasy użyteczności, które nie stanowią żadnego ryzyka wzdęcia, stanowią doskonałe przypadki dla metod statycznych - na przykład System.Convert. Jeśli Twój projekt jest jednorazowy i nie wymaga żadnych przyszłych prac konserwacyjnych, ogólna architektura naprawdę nie jest bardzo ważna - statyczna lub niestatyczna, nie ma większego znaczenia - jednak szybkość programowania ma znaczenie.

Standardy, standardy, standardy!

Korzystanie z metod instancji nie przeszkadza również w stosowaniu metod statycznych i odwrotnie. Tak długo, jak istnieje uzasadnienie różnicowania i jest ono znormalizowane. Nie ma nic gorszego niż przeglądanie warstwy biznesowej rozłożonej na różne metody wdrażania.

Stóg
źródło
12
Hmmm, nie wiem, że tutaj skopiuję i wkleję całą odpowiedź. Może link i parafraza zawierają alternatywny punkt widzenia, skoro wspominasz o „dyskusji”, dzielisz się swoimi doświadczeniami?
Corbin,
2
„Tylko Sith zajmuje się absolutami” - nigdy wcześniej nie widziałem takiego zastosowania… ale jest DOSKONAŁY. Zrobił mi lol.
Brook
4
@Corbin: Mark miał świetną odpowiedź. Przyznaję mu uznanie i skopiowałem / wkleiłem dla ułatwienia wyszukiwania informacji. Moje własne poglądy na ten temat są zgodne z poglądami Marka. Nie rozumiem sensu twojego komentarza poza rozpoczęciem debaty.
Rick
1
@ Rick: Skopiowana odpowiedź jest dość długa i parafrazowanie jej zdecydowanie by pomogło. Takie zdanie: „Nie chodzi tylko o to, że nie są tak naprawdę przydatne, mogą nawet zaszkodzić, jak pokazuje ten post z SO:” pozwoliłoby mi szybko pominąć, ponieważ widzę, że nie chodzi o informacje, których szukałem.
blubb
@Simon: LOL I 1 + 'edytował twój post. Próbując przestać kłócić się o coś tak głupiego, po prostu się zgodzę. :) Mam nadzieję, że oryginalny plakat uznał moją odpowiedź / kopię i wklej za przydatną.
Rick
16

Dziwię się, że nikt inny nie wspomniał, że staticklasy zezwalają na metody rozszerzania - bezpiecznie rozszerzając istniejący typ (w tym dodając definicje metod do interfejsów ), których nie posiadasz. Na przykład, czym jest Scala

trait MyFoo {
  def foo: Int
  def plusFoo(a: Int) = foo + a
}

można wyrazić w C # jako

public interface IMyFoo {
  int Foo();
}

public static class MyFooExtensions {
  public static int PlusFoo(this IMyFoo f, int a) {
    return f.Foo() + a;
  }
}
Frank Shearar
źródło
znalazłeś igłę w stogu siana. +1
Saeed Neamati
3

Dla mnie klasa statyczna (w języku C #) przypomina trochę wywoływanie funkcji.

Na przykład

public static string DoSomething(string DoSomethingWithThisString){}

Podaję sznurek i odzyskuję sznurek. Wszystko jest zawarte w tej metodzie. Brak dostępu do zmiennej członka itp.

Szczerze mówiąc, przede wszystkim jego zastosowanie polegało na zmniejszeniu linii kodu:

Dlaczego:

MyClass class = new MyClass();
String s = class.DoSomething("test");

Kiedy mogę to zrobić:

String s = MyClass.DoSomething("test");

Tak więc używałbym klas statycznych w tych przypadkach, gdy chciałem coś zrobić bez potrzeby klasy i oznaczałoby to mniej pisania.

Punkty Mark S. są poprawne, ale dla mnie, jeśli mam jakieś metody użyteczności, łatwiej udawać, że wywołuję funkcję, aby odwoływać się do nich jako jednej linijki.

To może być uproszczone, ale głównie tak wykorzystałem static.

Jon Raynor
źródło
1
nowa MyClass (). DoSomething („test”) jest nadal ważny. Często używam tego do testowania konstruktorów danych.
JoanComasFdz