"Singleton", ok czy źle?

Mam wiele (abstrakcyjnych) fabryk i są one zazwyczaj realizowane jako singletony.

Zwykle dla wygody, aby nie musieć przepuszczać ich przez warstwy, które naprawdę nie mają interesu w używaniu lub znajomości tych fabryk.

W większości przypadków muszę tylko podjąć decyzję przy starcie której implementacji fabrycznej resztę kodu programu, może poprzez jakąś konfigurację

Wygląda np. jak

abstract class ColumnCalculationFactory { 
  private static ColumnCalculationFactory factory;

 public static void SetFactory(ColumnCalculationFactory f) {
         factory = f;
  }

  public static void Factory() {
         return factory;
  }

 public IPercentCalculation CreatePercentCalculation();
 public IAverageCalculation CreateAverageCalculation();
    ....

}

Something do smell about this, I ' m just not pewnie , że to bardziej disuised global niż singleton. To nie jest tak, że naprawdę naprawdę musi być tylko jedna fabryka kiedykolwiek tworząca obliczenia kolumnowe-chociaż moje programy nie potrzebują więcej.

Czy jest to uważane za najlepszą praktykę ? Czy powinienem raczej wsadzić je do jakiejś (pół) globalnej klasy AppContext ? Coś innego(nie jestem do końca gotowy przełączyć się na jakiś większy kontener IoC, czy spring.net jeszcze BTW)?

Author: leeeroy, 2009-08-16

6 answers

To naprawdę zależy od tego, co robisz i zakresu aplikacji. Jeśli jest to tylko dość mała aplikacja i nigdy nie wzrośnie poza to, twoje obecne podejście może być w porządku. Nie ma uniwersalnej "najlepszej" praktyki dla tych rzeczy. Chociaż nie zalecałbym używania singletonów do niczego innego niż bezpaństwowe metody liścia i / lub jednokierunkowe wywołania( np. logowanie), odrzucenie go z ręki "tylko dlatego, że" jest singletonem niekoniecznie jest właściwą rzeczą zrób.

Dla czegokolwiek innego niż trywialny lub prototypowy kod, osobiście lubię wyraźnie używać inwersji sterowania z konstruktorem injection, ponieważ oznacza to, że wszystkie zależności są rozliczane i nie dostajesz żadnych "niespodzianek". Kompilator nie pozwoli Ci utworzyć instancję A bez B i B bez C. Singletony natychmiast zakopują te relacje - możesz utworzyć instancję a bez B i B bez C. gdy dojdzie do wywołania od A do B, otrzymasz null reference exception.

To jest szczególnie irytujące podczas testowania, ponieważ musisz iteracyjnie pracować wstecz przez awarie uruchomieniowe. Podczas testowania kodu używasz API tak, jak zrobiłby to inny koder, więc jest to wskaźnikiem problemów projektowych z tym podejściem. Constructor injection zapewnia, że tak się nie stanie - wszystkie zależności są podane z góry. Minusem constructor injection jest to, że konfiguracja wykresu obiektowego jest bardziej skomplikowana. Jest to złagodzone dzięki zastosowaniu IoC Pojemnik.

Myślę, że próbuję powiedzieć, że jeśli doszedłeś do punktu, w którym rozważasz użycie jakiegoś obiektu kontekstowego i wzorca rejestru, równie dobrze możesz rzucić okiem na kontenery IoC. Dążenie do wysiłku zwijania własnej wersji mutt jest prawdopodobnie stratą czasu, gdy można użyć uznanego, bezpłatnego produktu, takiego jak Autofac.

 11
Author: Mark Simpson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-16 20:18:25

Posiadanie kilku singletonów jest dość typowe i zazwyczaj nie problematyczne--wiele singletonów prowadzi do jakiegoś irytującego kodu.

Właśnie przeszliśmy przez sytuację, w której musieliśmy przetestować nasze mocno singleton-obciążone klasy. Problem polega na tym, że kiedy testujesz klasę b i dostaje klasę c (a singleton), nie masz sposobu, aby wyśmiewać klasę c (przynajmniej EasyMock nie pozwoliłby nam zastąpić statycznej metody fabrycznej klasy singleton.

Jedną prostą poprawką jest posiadanie " seterów" dla wszystkich singletonów do celów testowych. Nie polecam.

Inną rzeczą, którą próbowaliśmy było mieć jedną klasę, która przechowywała wszystkie singletony -- rejestr. Robi to coraz bliżej do iniekcji zależności, która jest tym, czego prawie na pewno powinieneś używać.

Poza testowaniem, dawno temu nauczyłem się, że kiedy nigdy nie będzie więcej niż jedna instancja danego obiektu; w następnej rev często chcą dwóch, co sprawia, że singletony znacznie lepsze niż klasy statyczne-przynajmniej możesz dodać parametr do gettera Singletona i zwrócić drugi bez zbytniego refaktoryzacji (co znowu robi to, co robi DI).

W każdym razie, spójrz na DI, możesz być naprawdę szczęśliwy.

 11
Author: Bill K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-16 19:42:18

Nie, bo to, co tu robisz, to tworzenie globalnego Państwa. Istnieją różnego rodzaju problemy z globalnym Państwem-głównym wśród nich jest to, że jedna funkcja zależy wtedy w dość niewidoczny sposób od zachowania innych funkcji. Jeśli funkcja wywołuje inną funkcję, która zapomina o przechowywaniu i przywracaniu factory przed jej zakończeniem, to masz problem, ponieważ nie możesz nawet odzyskać starej wartości, chyba że gdzieś ją przechowałeś. I trzeba do tego dodać kod (sądząc po Twoim kodzie, ja bym chyba jesteś w języku z finally, co pozostawia jeszcze więcej miejsca na błędy). Co więcej, jeśli skończysz z kodem, który musi szybko przełączać się między dwoma fabrykami dla dwóch podobiektów, musisz napisać wywołanie metody w każdym punkcie - nie możesz zapisać stanu w każdym podobiekcie(cóż, możesz ,ale wtedy pokonasz cel stanu globalnego [co prawda nie jest dużo]).

Prawdopodobnie najbardziej sensowne jest przechowywanie Typu fabrycznego jako elementu i przekazanie go konstruktorowi nowego obiekty, które go potrzebują (lub utworzyć nowy w razie potrzeby, itp.). Daje to również lepszą kontrolę - możesz zagwarantować, że wszystkie obiekty zbudowane przez obiekt A przeszły przez tę samą fabrykę, lub możesz zaoferować metody wymiany fabryk.

 6
Author: coppro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-16 19:29:09

Odradzałbym to, ponieważ bardzo trudno jest napisać przyzwoite testy jednostkowe dla kodu, który nazywa te fabryki.

Misko ma na swoim blogu fajny artykułna ten temat.

 4
Author: jqno,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-16 20:11:43
Czy jest to uważane za najlepszą praktykę?

Nie widzę problemu: powiedziałeś "moje programy nie potrzebują więcej", dlatego Twoje wdrożenie czegoś bardziej elastycznego / abstrakcyjnego może być przypadkiem nie będziesz tego potrzebował.

 3
Author: ChrisW,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-16 19:26:57

Zła forma Singletona to ta, która implementuje odpowiednik metody Fabrycznej z:

return new CalculationFactory ();
To znacznie trudniejsze do zakleszczenia, sfabrykowania lub owinięcia.

Wersja, która zwraca obiekt utworzony w innym miejscu jest znacznie lepsza, chociaż jak większość innych, może być nadużywana lub nadużywana.

 1
Author: soru,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-16 22:46:46