Czy nie mogę po prostu użyć wszystkich metod statycznych?

64

Jaka jest różnica między dwiema metodami UpdateSubject poniżej? Czułem, że stosowanie metod statycznych jest lepsze, jeśli chcesz po prostu operować na jednostkach. W jakich sytuacjach powinienem stosować metody niestatyczne?

public class Subject
{
    public int Id {get; set;}
    public string Name { get; set; }

    public static bool UpdateSubject(Subject subject)
    {
        //Do something and return result
        return true;
    }
    public bool UpdateSubject()
    {
        //Do something on 'this' and return result
        return true;
    }
}

Wiem, że otrzymam wiele kopnięć od społeczności za to naprawdę denerwujące pytanie, ale nie mogłem się powstrzymać od zadawania tego pytania.

Czy staje się to niepraktyczne w przypadku dziedziczenia?

Aktualizacja:
dzieje się teraz w naszym miejscu pracy. Pracujemy nad 6-miesięczną aplikacją asp.net z 5 programistami. Nasz architekt zdecydował, że stosujemy wszystkie metody statyczne dla wszystkich interfejsów API. Jego rozumowanie jako statyczne metody jest lekkie i przynosi korzyści aplikacjom internetowym, utrzymując obciążenie serwera na niskim poziomie.

Alexander
źródło
9
Rich Hickey zachęcałby do czegoś takiego niż idiomatyczne (wszystkie metody statyczne). Jeśli lubisz funkcjonalny styl, równie dobrze możesz użyć funkcjonalnego języka;) Nie wszystko w oprogramowaniu najlepiej modelować jako obiekt.
Job
13
@Job: Naprawdę nie rozumiem, jak to działa - jest proceduralne.
Aaronaught
2
@Job: Ta klasa nie jest niezmienna.
Aaronaught
3
@DonalFellows: Oczywiście możesz, C # i F # są paradygmatem mieszanym. Ale faktem pozostaje, metody statyczne! = Programowanie funkcjonalne. FP oznacza przekazywanie / tworzenie łańcuchów, niezmienne typy danych, unikanie efektów ubocznych itp. Jest to całkowicie odwrotne do tego, co robi fragment kodu w OP.
Aaronaught
4
„Jego rozumowanie jako statyczne metody jest lekkie i przynosi korzyści aplikacjom internetowym, utrzymując obciążenie serwera”. To jest złe. Utrzymanie obciążenia serwera?
mamcx

Odpowiedzi:

87

Przejdę do najbardziej oczywistych problemów metod statycznych z wyraźnymi parametrami „to”:

  1. Tracisz wirtualną wysyłkę, a następnie polimorfizm. Nigdy nie można zastąpić tej metody w klasie pochodnej. Oczywiście możesz zadeklarować metodę new( static) w klasie pochodnej, ale każdy kod, który uzyskuje do niej dostęp, musi być świadomy całej hierarchii klas oraz wykonywać jawne sprawdzanie i rzutowanie, czego dokładnie powinien unikać OO .

  2. W pewnym sensie rozszerzenie nr 1 nie można zastąpić instancji klasy interfejsem , ponieważ interfejsy (w większości języków) nie mogą deklarować staticmetod.

  3. Niepotrzebna gadatliwość. Co jest bardziej czytelne: Subject.Update(subject)czy po prostu subject.Update()?

  4. Sprawdzanie argumentów. Znowu zależy od języka, ale wielu skompiluje niejawne sprawdzenie, aby upewnić się, że thisargument nie ma nullna celu zapobieżenie stworzeniu przez błąd odniesienia zerowego niebezpiecznych warunków środowiska wykonawczego (rodzaj przepełnienia bufora). Nie korzystając z metod instancji, musisz jawnie dodać tę kontrolę na początku każdej metody.

  5. To jest mylące. Kiedy normalny, rozsądny programista zobaczy staticmetodę, będzie naturalnie zakładał, że nie wymaga ona prawidłowej instancji (chyba że wymaga wielu instancji, takich jak metoda porównania lub metoda równości, lub oczekuje się, że będzie w stanie działać na nullreferencjach) . Zobaczenie metod statycznych zastosowanych w ten sposób sprawi, że zrobimy podwójne, a może potrójne ujęcie, a po 4. lub 5. raz będziemy zestresowani i źli, a Bóg pomoże ci, jeśli znamy twój adres domowy.

  6. To forma powielania. To, co faktycznie dzieje się po wywołaniu metody instancji, polega na tym, że kompilator lub środowisko wykonawcze wyszukuje metodę w tabeli metod tego typu i wywołuje ją za thispomocą argumentu. Zasadniczo wdrażasz ponownie to, co kompilator już robi. Naruszasz DRY , powtarzając wielokrotnie ten sam parametr różnymi metodami, gdy nie jest to potrzebne.

Trudno wyobrazić sobie dobry powód do zastąpienia metod instancji metodami statycznymi. Proszę nie.

Aaronaught
źródło
5
+1 za samą linię końcową. Chciałbym, aby o wiele więcej osób pomyślało „metodę instancji”, zanim pomyślą „metodę statyczną”.
Stuart Leyland-Cole
4
+1. Dodałbym, że metody statyczne utrudniają pisanie testów, ponieważ w większości przypadków nie można ich wyśmiewać: gdy jakakolwiek część kodu zależy od metody statycznej, nie można zastąpić go nie- op w twoich testach. Dobrym przykładem w świecie .NET byłoby klas podoba File, FileInfoi DirectoryInfo(aw rzeczywistości istnieją biblioteki, które ukrywają je za interfejsów, które mogą być następnie wstrzykiwane jako potrzebne i wyśmiewany w testach).
sm
Myślę, że twoje spostrzeżenia na temat polimorfizmu / dziedziczenia są dyskusyjne. nie należy używać dziedziczenia do ponownego użycia kodu, więc nie ma to znaczenia. część spadkowa jest również wątpliwa. w większości współczesnych języków podpisy metod są same w sobie polimorficzne. jeśli zastosujesz podejście funkcjonalne, zaczniesz przekazywać metody i komponujesz złożone metody z prostych, które są wymienne na podstawie ich interfejsu. zasadniczo nie ma wad metod statycznych, które nie są kompensowane przez zwykłe projektowanie z myślą o nich, tak jak w przypadku OOP.
sara
@sm to po prostu nieprawda. jeśli masz twardą zależność od WSZYSTKIEGO, statycznego lub nie, to nie może być w teście jednostkowym, oznacza to, że kropka jest niestabilna. statyczny nie powoduje tego. metodę statyczną można wstrzyknąć w taki sam sposób, jak klasa reprezentująca połączenie db. zakładasz, że „statyczny” oznacza „statyczny plus mocno ukryte zależności dotykające stanu globalnego i zasobów zewnętrznych”. to niesprawiedliwe.
sara
@ kai spróbuj wstrzyknąć metodę statyczną, która robi Y zamiast X, niezależnie od X i Y. Nie musisz polegać na czymkolwiek zewnętrznym, aby zostać wkręconym. Być może jesteś zainteresowany testowaniem jakiegoś aspektu, który nie wymaga wykonywania długich obliczeń X. Jak wstrzyknąć fantazyjną metodę statyczną bez tworzenia opakowania? Przekazywanie funkcji nie jest obsługiwane przez każdy język. Metody statyczne mają swoje przypadki użycia i używam ich, gdy ma to sens . Jest to, jak to zawsze bywa, przypadek „tylko dlatego, że nie możesz koniecznie oznaczać, że powinieneś”.
sm
13

Prawie opisałeś dokładnie, jak robisz OOP w C, w którym dostępne są tylko metody statyczne, a „this” jest wskaźnikiem struktury. I tak, można wykonać polimorfizm w czasie wykonywania w C, określając wskaźnik funkcji podczas budowy, który ma być przechowywany jako jeden element struktury. Metody instancji dostępne w innych językach to po prostu cukier składniowy, który zasadniczo robi dokładnie to samo pod maską. Niektóre języki, takie jak python, znajdują się w połowie drogi, gdzie parametr „self” jest wyraźnie wymieniony, ale specjalna składnia do jego wywoływania obsługuje dla ciebie dziedziczenie i polimorfizm.

Karl Bielefeldt
źródło
FWIW, z C możesz wykonać wirtualną wysyłkę w ten sposób: obj->type->mthd(obj, ...);i jest to zasadniczo podobne do tego, co dzieje się z wirtualną wysyłką w innych językach (ale za kulisami).
Donal Fellows
@Karl Czy możesz podać jakieś pisemne odniesienie, aby zacząć kopać głębiej?
Jorge Lavín
Możesz spojrzeć na GObject jako dobrą implementację referencyjną.
Karl Bielefeldt,
10

Problem z metodą statyczną pojawia się w momencie, gdy potrzebujesz podklasy.

Metod statycznych nie można zastąpić w podklasach, dlatego nowe klasy nie mogą zapewnić nowych implementacji metod, co czyni je mniej użytecznymi.


źródło
2
To niekoniecznie problem. Niektóre języki zapewniają inne mechanizmy (nie-wirtualne metody w C ++ i C #) do celowego osiągnięcia tego samego celu - C # zapewnia nawet „zapieczętowane” metody dalszego rozszerzenia tego pomysłu! Oczywiście nie zrobiłbyś tego tylko do cholery, ale to dobra technika, aby zdawać sobie sprawę z ...
Shog9
@ Steve, który nie zastępuje, ponieważ nadal można bezpośrednio wywoływać obie metody.
@ Steve, w Javie metod statycznych nie można zastąpić.
@ Thorbjørn - po pierwsze, pytanie nie jest oznaczone Java, ani żaden inny konkretny język OOP. Co ważniejsze, nie powiedziałem, że można zastąpić statyczną funkcję członka. Próbowałem użyć analogii, aby coś powiedzieć. Ponieważ wyraźnie mi się nie udało, komentarze zostały usunięte.
Steve314,
2
W pewnym sensie są to nadal „nowe implementacje metod”. Po prostu nie w standardowym sensie OOP. To trochę tak, jakbym powiedział: „Twoje sformułowania też nie są idealne, nur-nur-na-nur-nur” ;-)
Steve314,
7

Jeśli deklarujesz metodę static, nie musisz tworzyć instancji obiektu z klasy (używając newsłowa kluczowego), aby wykonać metodę. Nie można jednak odwoływać się do żadnych zmiennych składowych, chyba że są one również statyczne, w którym to przypadku te zmienne składowe należą do klasy, a nie do określonego obiektu instancji klasy.

Innymi słowy, staticsłowo kluczowe powoduje, że deklaracja jest częścią definicji klasy, więc staje się pojedynczym punktem odniesienia we wszystkich instancjach klasy (obiekty tworzone z klasy).

Wynika z tego, że jeśli chcesz wielu uruchomionych kopii swojej klasy i nie chcesz, aby stan (zmienne składowe) był współużytkowany przez wszystkie uruchomione kopie (tj. Każdy obiekt ma swój własny unikalny stan), wtedy nie można zadeklarować tych zmiennych składowych jako statycznych.

Zasadniczo powinieneś używać staticklas i metod tylko wtedy, gdy tworzysz metody narzędziowe, które akceptują jeden lub więcej parametrów i zwracają jakiś obiekt, bez żadnych skutków ubocznych (tj. Zmiany zmiennych stanu w klasie)

Robert Harvey
źródło
1
Rozrzedzam, że OP rozumie, co to staticznaczy, pyta, dlaczego nie zadeklarować wszystkiego jako statycznego.
Ed S.
1
On jest przechodzącą w instancji Subject- ale jako wyraźną parametru, zamiast niejawnym thisparametrem.
Steve314,
Cóż, tak ... ale nie rozumiem, o co ci chodzi.
Ed S.
@Ed - tylko, że możesz zrobić prawie wszystko za pomocą jawnego parametru, co możesz zrobić za pomocą thisparametru niejawnego . Nie ma różnic w oczekiwaniach, ale poza dziedziczenia, nie ma istotnej różnicy w tym, co można zrobić. Na przykład, do tych niestatycznych elementów członkowskich można uzyskać dostęp - za pomocą jawnego parametru.
Steve314,
Działałem przy założeniu, że nie można dostać się do prywatnych członków argumentu w C #, nawet za pomocą metody statycznej zadeklarowanej w tej samej klasie (jak można to zrobić w C ++). Nie jestem pewien, dlaczego tak myślałem, ale się myliłem.
Ed S.
3

Powód, dla którego ludzie twierdzą, że „metody statyczne wykazują zły projekt” niekoniecznie wynika z metod, w rzeczywistości są to dane statyczne, niejawne lub jawne. Przez domniemany rozumiem DOWOLNY stan w programie, który nie jest zawarty w zwracanych wartościach lub parametrach. Modyfikowanie stanu w funkcji statycznej to powrót do programowania proceduralnego. Jeśli jednak funkcja nie modyfikuje stanu lub przekazuje modyfikację stanu do funkcji obiektu składowego, w rzeczywistości jest to bardziej funkcjonalny paradygmat niż proceduralny.

Jeśli nie zmieniasz stanu, metody statyczne i klasy mogą przynieść wiele korzyści. Znacznie ułatwia to, aby metoda była czysta, a zatem wolna od skutków ubocznych, a wszelkie błędy są w pełni powtarzalne. Zapewnia również, że różne metody w wielu aplikacjach korzystających ze wspólnej biblioteki mogą być pewne, że uzyskają te same wyniki przy tych samych danych wejściowych, co znacznie ułatwi zarówno śledzenie błędów, jak i testowanie jednostek.

Ostatecznie w praktyce nie ma różnicy między często przewymiarowanymi obiektami, takimi jak „StateManager”, a danymi statycznymi lub globalnymi. Zaletą prawidłowych metod statycznych jest to, że mogą one wskazywać na intencję autora, aby nie modyfikować stanu późniejszych korektorów.

Mathieson
źródło
1
Twoje stwierdzenie „... jest powrotem do programowania proceduralnego ...” sprawia, że ​​myślę, że uważasz to za tak złe, myślę, że w pewnym sensie jest częścią OO.
NoChance
making ... unit testing much easier.Nie, nie będzie. To sprawia, że ​​żaden kod wywołujący metody statyczne jest niemożliwy do testowania jednostkowego , ponieważ nie można go odizolować od metody statycznej. Wywołanie metody statycznej staje się na stałe zależną od tej klasy.
StuperUser,
2

W zależności od języka i tego, co próbujesz zrobić, odpowiedź może być taka, że ​​nie ma dużej różnicy, ale staticwersja ma nieco więcej bałaganu.

Ponieważ twoja przykładowa składnia sugeruje, że Java lub C #, myślę, że Thorbjørn Ravn Andersen słusznie wskazuje na nadrzędny (z późnym wiązaniem) problem. W przypadku metody statycznej nie ma „specjalnego” parametru, na którym powinno się opierać późne wiązanie, więc nie można mieć późnego wiązania.

W efekcie metoda statyczna jest po prostu funkcją w module, który ma taką samą nazwę jak klasa - tak naprawdę wcale nie jest to metoda OOP.

Steve314
źródło
2

W ten sposób pokonujesz cały punkt obiektów. Jest dobry powód, dla którego właściwie zmieniliśmy kod proceduralny na kod obiektowy.

Ja nigdy nie deklarują statyczną metodę, która miała typ klasy jako parametr. To tylko sprawia, że ​​kod jest bardziej złożony bez żadnego zysku.

Loren Pechtel
źródło
3
Można to zrobić, jeśli były przepuszczanie objectdo staticsposobu i powrocie nowa object. Funkcjonalni programiści robią to cały czas.
Robert Harvey
pytanie: czy uważasz Mathklasę statyczną za „kompleks bez wzmocnienia”, czy wolałbyś, gdyby wszystkie typy liczb miały metody wirtualne określające wartość bezwzględną, negację, dodawanie, kwadratowanie, arbitralne pierwiastki i tak dalej? Nie wydaje mi się obiekty są schludne, gdy trzeba przechowywać ukryty zmienny stan, w którym należy wymusić niektóre niezmienniki. łącząc każdą możliwą metodę, która działa na typie z samym typem, TO jest coś, co prowadzi do złożonego i niemożliwego do utrzymania kodu. Zgadzam się z Robertem, możesz spojrzeć na to, jak działają funkcjonalni programiści, może to być naprawdę przyciągające wzrok!
sara
@kai Jak już powiedziałem, prawie. Klasa Math jest rozsądnym wyjątkiem, chociaż dołączenie jej do typów numerycznych nie byłoby złym pomysłem.
Loren Pechtel
2

Jest tutaj wiele świetnych odpowiedzi i są one o wiele bardziej wnikliwe i oparte na wiedzy niż cokolwiek, co kiedykolwiek mogłem wymyślić jako odpowiedź, ale czuję, że jest coś, o czym nie ma mowy:

„Nasz architekt zdecydował, że stosujemy wszystkie metody statyczne dla wszystkich interfejsów API. Jego rozumowanie jako metody statyczne jest niewielkie i przynosi korzyści aplikacjom internetowym, utrzymując obciążenie serwera ”.

(śmiały nacisk jest mój)

Moje 2c w tej części pytania brzmi:

Teoretycznie to, co jest powiedziane, jest prawdą: wywołanie metody statycznej ma jedynie narzut związany z faktycznym wywołaniem tej metody. Wywołanie metody niestatycznej (instancji) wiąże się z dodatkowym nakładem pracy związanym z pierwszą instancją obiektu, aw pewnym momencie zniszczeniem instancji (ręcznie lub za pomocą jakiejś formy automatycznego wyrzucania elementów bezużytecznych, w zależności od używanej platformy).

Zagraj trochę rzecznika diabła w tym: możemy pójść jeszcze dalej i powiedzieć takie rzeczy jak:

  • może to stać się naprawdę złe, jeśli instancja jest tworzona dla każdego wywołania metody (instancji) (w przeciwieństwie do pozostawienia jej statycznego i wywołania w ten sposób)

  • w zależności od złożoności konstruktora, rodzaju herarchii, innych elementów instancji i innych takich nieznanych czynników, dodatkowe obciążenie związane z niestatycznym wywołaniem metody może się różnić i stać się naprawdę duże

Prawdę mówiąc, IMHO, powyższe punkty (i ogólne podejście „używajmy statyki, ponieważ są one szybsze”) są argumentami / ocenami słomianego człowieka:

  • jeśli kod jest dobry i, bardziej konkretnie dla tego argumentu, jeśli instancje są tworzone tylko wtedy, gdy jest to konieczne (i niszczone w optymalny sposób), wówczas nie ma dodatkowego obciążenia związanego z używaniem metod ubezpieczenia, gdy jest to właściwe (ponieważ jeśli twórz tylko potrzebne obiekty, zostałyby one utworzone, nawet gdyby ta metoda została zadeklarowana jako statyczna, gdzie indziej = narzut ten sam narzut)

  • przez nadużywanie deklaracji metod statycznych w ten sposób można ukryć pewne problemy z kodem w odniesieniu do sposobu tworzenia instancji (ponieważ metoda jest statyczna, niepoprawny kod instanencji może przejść niezauważony do później, a to nigdy nie jest dobre).

Shivan Dragon
źródło
2

To bardzo interesujące pytanie, które zazwyczaj ma bardzo różne odpowiedzi w zależności od społeczności, o którą pytasz. Inne odpowiedzi wydają się być stronnicze w języku C # lub java: język programowania, który jest (głównie) czysty obiektowo i ma rodzaj idiomatycznego poglądu na to, czym jest orientacja obiektowa.

W innych społecznościach, takich jak C ++, orientacja obiektowa jest interpretowana z większą swobodą. Niektórzy znani eksperci studiowali ten temat i faktycznie stwierdzili, że wolne funkcje poprawiają enkapsulację (nacisk jest mój):

Widzieliśmy teraz, że rozsądnym sposobem oceny stopnia enkapsulacji w klasie jest policzenie liczby funkcji, które mogą zostać uszkodzone, jeśli zmieni się implementacja klasy. W związku z tym staje się jasne, że klasa z n funkcjami składowymi jest bardziej enkapsulowana niż klasa z n + 1 funkcjami składowymi. Ta obserwacja uzasadnia mój argument za preferowaniem funkcji nieprzyjaznych niebędących członkami niż funkcji członkowskich

Scott Meyers

Dla osób niezaznajomionych z terminologią C ++:

  • funkcja członka = metoda
  • funkcja nie będąca członkiem = metoda statyczna
  • non-friend = użyj tylko publicznego API
  • non-member non-friend function = metoda statyczna, która używa tylko publicznego API

Teraz, aby odpowiedzieć na pytanie:

Czy nie mogę po prostu użyć wszystkich metod statycznych?

Nie, nie zawsze należy stosować metody statyczne.

Ale powinieneś ich używać, ilekroć potrzebujesz tylko publicznego interfejsu API klasy.

authchir
źródło
-3

w Javie ...

Metody statyczne nie są nadpisywane, ale są ukryte, dlatego duża różnica (problem?) W przypadku rozszerzenia klasy byłaby duża.

Z drugiej strony zauważyłem, że programiści Java mają tendencję do myślenia, że ​​wszystko MUSI BYĆ przedmiotem, nie do końca rozumiejąc dlaczego, i nie zgadzam się.

W przypadku kodu specyficznego dla klasy należy zastosować wartość statyczną. Statyczne nie działa dobrze z dziedziczeniem.

Dobrymi przykładami zastosowania metod statycznych są niezależne funkcje bez skutków ubocznych.

zobacz także słowo kluczowe zsynchronizowane ...

Nicolas
źródło
2
Jak ukrywa się ta metoda statyczna i gdzie pojawi się ta różnica i problem podczas rozszerzania klasy? W jaki sposób synchronizuje się z tym? Gdzie można napotkać problemy statyczne i dziedziczenia? Czy możesz podać dobre i złe metody statyczne w podstawowych bibliotekach Java i wyjaśnić, dlaczego tak jest?