Static class / Sigleton a rzeczywistość

0

Czy ktoś może mi wyjaśnić jak się ma stosowanie singletona oraz klass statycznych w rzeczywistości.
Nie rozumiem dlaczego w książkach, kursach video, a nawet na stronie MS namawiają do tej praktyki, skoro jest to naruszeniem podstawowej zasady SOLID jak Open/Closed oraz Single Responsibility o TDD nie wspomnę ponadto ma się to nie jako do programowania obiektowego. Czy nie lepiej jest użyć do tego Kontenera IOC?.

To przykład z książki ".NET Domain-Driven Design with C# "

The first thing to notice about this class is that it is a static class with all static methods. Again, the idea is to make it very easy to use.

W tej książce każda fabryka czy serwis jest klasą statyczną. :)

Interesuje mnie to, w jaki sposób wy piszecie kod i jak to się robi w firmach. Czy w rzeczywistości przyświeca temu idea z tej książki?

I teraz ktoś może mi powiedzieć, że to jest przykład, ale gdy komuś naprawdę zależy, żeby pisać dobry kod od takich przykładów "pląta się w głowie"

4
  1. Dlaczego narusza Open/Closed?
  2. Dlaczego narusza SRP?
  3. Dlaczego narusza TDD?
  4. Dlaczego singleton ma się „nie jako do programowania obiektowego” (i co w ogóle to znaczy)?
3

Nie znam tej książki, a cytat który przytoczyłeś jest poprawny, ciężko mi się więc wypowiedzieć. Oczywiście w książkach, kursach i nawet przykładach MS można znaleźć wiele złych praktyk, uczących złych nawyków, wykluczających się z innymi przykładami itd. Singletony, klasy/metody statyczne, kontenery IoC same w sobie nie są ani dobre, ani złe - jedynie można użyć ich w sposób dobry lub zły. Jak @Afish pytał - w jaki sposób naruszane są zasady SOLID? Owszem, można tak zaimplementować singletona/klasę statyczną, że zasady te będą naruszone, ale to nie wynika z żadnej definicji, ale po prostu z niewiedzy i/lub lenistwa.
Za chwilę weźmiesz do ręki inną książkę/kurs i przeczytasz, że static to zło, pod żadnym pozorem nie używaj. I pewnie w 90% przypadkach byłaby to prawda, bo sam wiele razy widziałem kod, gdzie static był źle użyty, ale to również wynikało głównie z niewiedzy.

0
Afish napisał(a):
  1. Dlaczego narusza Open/Closed?
  2. Dlaczego narusza SRP?
  3. Dlaczego narusza TDD?
  4. Dlaczego singleton ma się „nie jako do programowania obiektowego” (i co w ogóle to znaczy)?

Nie zrozumiałeś mnie mówię, że jedno, jak i drugie ma swoje wady.

1, 4. Klasa statyczna nie może korzystać z poliformizmu.
2. Singleton tworzy instancje w odniesieniu się do obiektów, w których nie będzie używany bez powodu. Poza tym tworzenie obiektów to problem globalny, a nie klasy, która ów obiekt definiuje i powinien być realizowany przez np. kontener IOC.
3. Ze względu na punkt 1 mogą wystąpić problemy z zastąpieniem kodu produkcyjnego testowym.

0

Tekst z książki odnosi się do fabryk oraz serwisów. Ale pytanie, jakie się nasuwa to, czy aby na pewno jest to najlepsze podejście do pisania kodu. Bo jeśli nie, to autor po prostu utrwala stosowanie złych praktyk czytlnikowi. Kod można pobrać na stronie autora za darmo jak ktoś ma ochotę.

0
mstl napisał(a):

Nie znam tej książki, a cytat który przytoczyłeś jest poprawny, ciężko mi się więc wypowiedzieć. Oczywiście w książkach, kursach i nawet przykładach MS można znaleźć wiele złych praktyk, uczących złych nawyków, wykluczających się z innymi przykładami itd. Singletony, klasy/metody statyczne, kontenery IoC same w sobie nie są ani dobre, ani złe - jedynie można użyć ich w sposób dobry lub zły. Jak @Afish pytał - w jaki sposób naruszane są zasady SOLID? Owszem, można tak zaimplementować singletona/klasę statyczną, że zasady te będą naruszone, ale to nie wynika z żadnej definicji, ale po prostu z niewiedzy i/lub lenistwa.
Za chwilę weźmiesz do ręki inną książkę/kurs i przeczytasz, że static to zło, pod żadnym pozorem nie używaj. I pewnie w 90% przypadkach byłaby to prawda, bo sam wiele razy widziałem kod, gdzie static był źle użyty, ale to również wynikało głównie z niewiedzy.

Przepraszam ale nie mogę sobię wyobrazić singletona który nie narusza zasad SOLID. Możesz podać jakiś przykład ?

4
  1. Wszystko jest dla ludzi.
  2. Jakby static było złem wcielonym to zostałby usunięty z języków programowania ;)
  3. Klasy statyczne są ok w pewnych szczególnych sytuacjach, ale jeśli pół twojego systemu jest o nie oparte to chyba zrobiłeś coś źle.

Static dobrze nadaje się na jakieś proste Utilsy których nie trzeba będzie nigdy "mockować" ani podmieniać na inne implementacje. StringUtils to dobry przykład. Trudno mi sobie wyobrazić sytuacje żebyś musiał w teście to mockować, albo żebyś kiedyś musiał podmienić implementacje na jakaś inną.
Jeśli chodzi o fabryki to trudno to zrealizować inaczej po prostu. Z kontenerem IoC radzę uważać bo związuje cię mocno z technologią i utrudnia w pewnych sytuacjach re-użycie. Jeśli chcesz coś udostępnić jako bibliotekę to raczej nie będziesz tam wpychał IoC, bo jest spora szansa na konflikty u późniejszych użytkowników tej biblioteki. IoC jest ok jeśli chcesz "poskładać" swoją aplikację i wystartować w łatwy sposób.

0

Jest kilka problemów ze statycznymi metodami:

  • jako że nie są polimorficzne, to nie można w sensowny sposób chociażby ich opakować - na przykład jeśli mam jakieś cache, które powinno mieć tylko jedną instancję (powiedzmy że jest thread-safe), i zrobię to sobie na metodach statycznych (bo mogę), to żeby wprowadzić jakąkolwiek zmianę (np chcę żeby mi logowało cache hits/misses, albo dziabało jakieś inne statystyki) muszę zmieniać kod. Open/Closed wyleciało przez okno, tak samo jak Liskov's substitution.

  • trudno je testować, zwłaszcza jeśli są stanowe - z powodów wylistowanych wyżej nie za bardzo można sobie je zastubować do testów, bywają jaja przy mockowaniu (nie wszystkie libki to wspierają).

  • statyczne metody nie mogą być częścią interfejsów, w związku z czym ciągle trzeba łazić do tej konkretnej implementacji. Nie jest to może zbrodnia, ale jest to trochę nieeleganckie.

0

@WebJarek:
Aaa, ok rozumiem, bo Tobie chodzi, żeby coś dziedziczyć po singletonie... No tak to nie, to faktycznie singleton nie jest od tego :D Z tym, że zasada O/C mówi tak:
klasy, moduły, funkcje itd. powinny być otwarte na rozszerzenie, ale zamknięte na modyfikacje
I ja to rozumiałem w ten sposób - mam sobie singletona:

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy =
        new Lazy<Singleton>(() => new Singleton());

    public IEnumerable<IWorker> Workers { get; }
    public static Singleton Instance => lazy.Value;

    private Singleton()
    {
        this.Workers = GetAllWorkersOfTheWorld();  //w jakiś sposób sobie pobieram
    }
}

I teraz mogę sobie dodawać implementacje IWorker ile chcę, bez ruszania singletona.

9

Jeśli metoda nie operuje na stanie obiektu, to robię ją statyczną. Jeśli wszystkie metody w danej klasie są statyczne, to i klasę robię statyczną. (No chyba, że nie mogę, bo musi być mockowana w testach.) Albo odwrotnie - tworzę klasę statyczną, bo od razu wiem, że nie będzie w niej metod operujących na stanie.
Przykłady to np. przetwarzanie stringów, różnego rodzaju operacje związane z refleksją (np. pobranie wszystkich metod udekorowanych danym atrybutem), konwersje między obiektami.

Sam ręcznie singletonów nie tworzę, bo jestem na to zbyt leniwy. Jeśli potrzebuję jednej instancji jakiegoś obiektu, to rejestruję w kontenerze IoC jako singleton. Przykładem może być np. globalna konfiguracja aplikacji, jakiś util posiadający stan, albo niestatyczny z innego względu.

Problem jest wtedy, gdy ktoś w statycznej klasie/singletonie ma stan dostępny globalnie w aplikacji i próbuje go zmieniać w różnych miejscach. No, ale jak ktoś chce sobie odstrzelić stopę razem z jajami, to żaden język programowania mu nie przeszkodzi.

0

1, 4. Klasa statyczna nie może korzystać z poliformizmu.

1 Sama nie może być polimorficzna, ale spokojnie może korzystać z polimorfizmu innych klas chociażby przez wzorzec strategia, więc jak najbardziej można realizować OCP.
4 Tutaj pytałem o singleton, nie o klasę statyczną.

  1. Singleton tworzy instancje w odniesieniu się do obiektów, w których nie będzie używany bez powodu. Poza tym tworzenie obiektów to problem globalny, a nie klasy, która ów obiekt definiuje i powinien być realizowany przez np. kontener IOC.

2 Czyli jak chcę policzyć sinusa, to potrzebuję kontenera IoC?

  1. Ze względu na punkt 1 mogą wystąpić problemy z zastąpieniem kodu produkcyjnego testowym.

3 Czyli jeżeli użyję klasy statycznej, to już nie jestem w stanie pisać najpierw testów i robić RGR?

Moim zdaniem mieszasz pojęcia i na siłę szukasz problemów. To nie jest tak, że klasa statyczna lub singleton są same w sobie złe i łamią jakieś zasady, to sposób ich użycia może być zły i łamać zasady.

2

w praktyce singletonow uzywam praktycznie wylacznie do null obj patternu. nie chce mi sie bawic w robienie innych klas singletonami bo to bylby przerost formy nad trescia. klasy statyczne to kontenery na powiazane ze soba utilsy (ByteUtils, MathUtils, StringUtils etc) albo jakies stale.

0

Następna odpowiedź ze stacka odnośnie:

[Afish napisał(a)](https://4programmers.net/Fo

Moim zdaniem mieszasz pojęcia i na siłę szukasz problemów. To nie jest tak, że klasa statyczna lub singleton są same w sobie złe i łamią jakieś zasady, to sposób ich użycia może być zły i łamać zasady.

https://softwareengineering.stackexchange.com/questions/101102/are-static-classes-with-static-methods-considered-solid

Single responsibility principle: a static class easily follow this principle.
Open/closed principle: since static classes are sealed, they cannot ever follow this principle.
Liskov substitution principle: as above.
Interface segregation principle: doesn't apply to a single class, but splitting one large static class into smaller, more specialized ones could be a step towards following this principle.
Dependency inversion principle: static classes cannot implement interfaces, so any class using it will always depend on whatever implementation exists at the time. Static classes therefore violate this principle.

Since static classes do not satisfy all 5 criteria, they are not SOLID.

Czyli rozumiem, że kurczowe trzymanie się solid to czasem przerost nad treścią...?
A jak bym zamiast static class użył kontenera IOC to coś by się złego stało ?? Wydaje mi się, że raczej byłoby to na plus niż minus.?
Przecież i tak w każdej klasie nie będę używał tego "statica czy siglentona"

2

Ale jak Ci tu kontener IOC pomoże? De facto jedną z rzeczy za które nie lubię kontenerów jest to, że potem masz cały projekt oparty o statyczne/singletony (Spring).
Co w tym jest lepszego od zwykłego Singletona ? Singletony są tylko ukryte pod pozorem normalnych klas. Ale Beany normalne nie są.

4

Zamiana klasy statycznej na beana IoC brzmi trochę jak zamiana wrotek na traktor. Niby oba to jakiś środek lokomocji, ale już na pierwszy rzut oka widać że zastosowanie jest nieco inne ;)
@jarekr000000 przesadzasz ;) Jak ktoś cały projekt zrobi na singletonowych beanach to bez IoC zrobiłby dokładnie to samo albo na staticach albo ręcznych singletonach. To nie technologia tu jest problemem tylko zasada "jak masz młotek to wszystko wygląda jak gwoździe".

1

Czyli rozumiem, że kurczowe trzymanie się solid to czasem przerost nad treścią...?

Może nie tyle przerost formy nad treścią, co dobrze jest wiedzieć, kiedy warto coś komplikować (przez zasady, wzorce), a kiedy nie ma to sensu. Jeżeli wiem, że nie wymienię implementacji czegoś na inną i nie będę tego używał gdzieś indziej, i mogę to przetestować, to czy potrzebuję pakować to w kontener IoC, czy wystarczy zwykła klasa statyczna? Czy Math.Sin mam wywalać do jakiegoś ISinCalculator i wstrzykiwać?

A jak bym zamiast static class użył kontenera IOC to coś by się złego stało ?? Wydaje mi się, że raczej byłoby to na plus niż minus.?

Zazwyczaj kontener IoC się przydaje, ale trzeba mieć umiar, bo potem powstają takie potworki, gdzie każdy interfejs ma dokładnie jedną implementację i wszystko jest jakoś magicznie składane do kupy nie wiadomo gdzie. To jest temat na troszkę szerszą dyskusję, która zresztą całkiem niedawno była na tym forum, dotyczyła testowania i kontenerów IoC.

Przecież i tak w każdej klasie nie będę używał tego "statica czy siglentona"

To już trzeba konkretne przypadki rozpatrywać. Moim zdaniem Twoje stwierdzenie, że static/singleton łamie wszystkie zasady i jest be jest zbyt ogólne — z pewnością da się ich źle użyć, ale to nie znaczy, że są one złe z natury.

A jeszcze co do rozkmin, czy statyczna klasa jest SOLID:
SRP — nie łamie
OCP — może używać strategii czy czegoś innego, więc spokojnie da się ją rozszerzać bez modyfikacji.
LSP — skoro nie da się podmienić tej klasy na klasę pochodną (bo i dziedziczyć się nie da), to i nie da się złamać tej zasady
ISP — mam statyczną klasę z jedną metodą i jest to jak najbardziej zgodne
DI — no i tego spełnić nie możemy
Czyli wynika, że jedynie DI jest problematyczne, więc formalnie SOLID-a nie spełnia, aczkolwiek jak używasz klas statycznych do odpowiednich rzeczy, to brak DI nie jest problemem.

3
WebJarek napisał(a):

Since static classes do not satisfy all 5 criteria, they are not SOLID.

To stwierdzenie jest po prostu bezsensowne. SOLID dotyczy programowania obiektowego, klasy statyczne z definicji nie są zgodne z tym paradygmatem.
I generalnie, to nawet jeśli klasa A korzysta z klasy B, nie oznacza od razu, że jest od niej zależna - może po prostu być jej właścicielem.

A jak bym zamiast static class użył kontenera IOC to coś by się złego stało ?? Wydaje mi się, że raczej byłoby to na plus niż minus.?
Przecież i tak w każdej klasie nie będę używał tego "statica czy siglentona"

Tylko po co? Prościej wywołać metodę statyczną niż dodać kolejne pole do wstrzykiwania przez konstruktor.

0

Ogólnie singletony to coś, czego w teorii nie powinno się używać. W praktyce różnie, jeśli miałbym się chrzanić z jakąś większą zabawą tylko po to, by uniknąć jednego singletona to ja to sobie daruję.

Ot, przykład z życia. Okienko w Swingu, pasek wyszukiwania i lista wyników - apka wyklepana w 3h dla programistycznie niepiśmiennych kolegów w pracy. Każdy użytkownik może skorzystać z aktualnych ustawień (zapisane w bazie danych) lub skorzystać z danych w pliku. W main'ie jeszcze przed stworzeniem JFrame ładuję te ustawienia do singletona. Dzięki temu nie trzeba było w każdej klasie wstrzykiwać tych ustawień. Czemu nie DI? Taki Spring przykładowo opóźniłby start apki o pewnie kilka ładnych sekund, nawet bez skanowania pakietów. Apka działa, ludzie korzystają, w zależnościach ma tylko sterownik JDBC.

Złamałem SOLID? Wisi mi to. W zamian okienko pojawia się sekundę po dwukliku.

0
wartek01 napisał(a):

Ogólnie singletony to coś, czego w teorii nie powinno się używać.

Zupełnie się z tym nie zgadzam. To, że ktoś tak kiedyś powiedział, bo być może sam nie umiał ich dobrze użyć albo widział złe użycia innych, nie oznacza, że trzeba tego słuchać. Wiem, co piszesz dalej w swoim poście, ale nie podoba mi się to, że "nie powinno się używać" i, że to "antywzorzec".

W szkole prawdopodobnie wszystkich uczą, że "nie zaczyna się zdania od więc". I to jest błędna nauka. Bo są wyjątki, które to dopuszczają. Mówią tak, bo im się nie chce tłumaczyć.

Tak samo jest z singletonem. Są przypadki jego użycia i gadanie, że się nie powinno jest po prostu.. bez sensu. Chociażby jakieś utilsy, czy coś takiego. Trzeba zacząć uczyć ludzi, kiedy się używa singletonów, a nie, że nie wolno i ch...

1

Warto przeczytać: http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars/ (i follow-upy).

Kiedy singleton jest bezstanowy, to prawdopodobnie nie jest jeszcze duży kłopot.

Jeśli reprezentuje stan globalny, może nastręczyć problemów np. przy pisaniu testów. Poza tym singletoństwo jest "zaraźliwe", na zasadzie prawa Kopernika-Greshama.

wartek01 napisał(a):

Czemu nie DI? Taki Spring przykładowo opóźniłby start apki o pewnie kilka ładnych sekund

Pomieszanie pojęć. DI jest tylko wzorcem i jako takie nie wymaga ani Springa, ani w ogóle żadnego frameworka ; ) Przekazywanie parametru w konstruktorze to już jest - z definicji - wstrzykiwanie zależności.

0
WebJarek napisał(a):

Poza tym tworzenie obiektów to problem globalny, a nie klasy, która ów obiekt definiuje i powinien być realizowany przez np. kontener IOC.

A czy kontener IOC może być singletonem? :)))

1 użytkowników online, w tym zalogowanych: 0, gości: 1