Kiedy używać konstruktora, a kiedy metody getInstance () (statyczne metody fabryczne)?

84
  1. Kiedy i jak powinniśmy używać konstruktora

    Foo bar = new Foo();
    
  2. A kiedy i jak powinniśmy używać getInstance () (statyczne metody fabryczne)

    Foo bar = Foo.getInstance();
    

Jaka jest różnica między tymi dwoma? Zawsze używałem konstruktora, ale kiedy powinienem użyć getInstance()zamiast niego?

zengr
źródło
Czy sam piszesz zajęcia? Jeśli nie, co nazywasz, że to zapewnia?
Chris B.
więc implementacja samej klasy oznacza, że ​​implementuję wzorzec singleton, prawda?
zengr
Czy powołanie getInstance() , czy może pisać metodę o nazwie getInstance()?
Chris B.
1
Jeśli pytanie dotyczy metod konstruktora kontra statyczne metody fabryczne , proponuję wyjaśnić i zmienić tytuł.
Pascal Thivent
@zengr: jeśli chodzi o twoją aktualizację2, może to być spowodowane tym, że nie nazwałeś swojej metody statycznej zgodnie z konwencją, która mówi, że należy ją nazwać Foo.newInstance(). Foo.getInstance()jest konwencją uzyskiwania pojedynczej instancji klasy. Powinieneś poprawić swój przykład i użyć Foo.newInstance()zamiast tego.
JRL,

Odpowiedzi:

96

Wydaje się, że wszyscy skupiają się na singletonach, podczas gdy ja myślę, że tak naprawdę pytanie dotyczy konstruktora kontra statyczne metody fabryczne .

To jest rzeczywiście Pozycja 1: Rozważmy statycznych metod fabrycznych zamiast konstruktorów z Effective Java przez Joshua Bloch:

Punkt 1: Rozważ statyczne metody fabryczne zamiast konstruktorów

Normalnym sposobem dla klasy, aby umożliwić klientowi uzyskanie jej wystąpienia, jest udostępnienie publicznego konstruktora. Jest jeszcze jedna technika, która powinna być częścią zestawu narzędzi każdego programisty. Klasa może udostępniać publiczną statyczną metodę fabryki , która jest po prostu statyczną metodą zwracającą instancję klasy. Oto prosty przykład z Boolean(klasa pierwotna w pudełku dla typu pierwotnego boolean). Ta metoda tłumaczy wartość logiczną pierwotną na Booleanodwołanie do obiektu:

public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE;
}

Zwróć uwagę, że statyczna metoda fabryczna to nie to samo, co wzorzec Factory Method z Design Patterns [Gamma95, str. 107]. Statyczna metoda fabryki opisana w tej pozycji nie ma bezpośredniego odpowiednika we wzorcach projektowych .

Klasa może udostępniać swoim klientom statyczne metody fabryczne zamiast konstruktorów lub oprócz nich. Dostarczenie statycznej metody fabryki zamiast publicznego konstruktora ma zarówno zalety, jak i wady.

Zalety (cytując książkę):

  • Jedną z zalet statycznych metod fabrycznych jest to, że w przeciwieństwie do konstruktorów mają one nazwy.
  • Drugą zaletą statycznych metod fabryki jest to, że w przeciwieństwie do konstruktorów nie są one wymagane do tworzenia nowego obiektu za każdym razem, gdy są wywoływane.
  • Trzecią zaletą statycznych metod fabrycznych jest to, że w przeciwieństwie do konstruktorów mogą zwracać obiekt dowolnego podtypu zwracanego typu.
  • Czwartą zaletą statycznych metod fabryki jest to, że zmniejszają one szczegółowość tworzenia sparametryzowanych wystąpień typu.

Wady (nadal cytując książkę):

  • Główną wadą dostarczania tylko statycznych metod fabrycznych jest to, że klas bez publicznych lub chronionych konstruktorów nie można tworzyć podklas.
  • Drugą wadą statycznych metod fabrycznych jest to, że nie można ich łatwo odróżnić od innych metod statycznych.
Pascal Thivent
źródło
8
Ten ostatni można nieco złagodzić. Zwykle mam statyczną klasę wewnętrzną o nazwie Factory i różne metody newInstance, aby było jaśniejsze podczas tworzenia metod fabrycznych. W przeszłości zaoszczędziło mi to szukania czasu. :)
Rich Schuler
@Qberticus wewnętrzna klasa dedykowana dla metod fabrycznych to fajny pomysł. Spróbuję, dzięki.
Dave O.,
Z pewnością konstruktory klas muszą przynajmniej protectedumożliwiać tworzenie podklas, ale czy kod klienta uzyskuje jakąkolwiek rzeczywistą korzyść z tworzenia nowego obiektu i wywoływania jego konstruktora w porównaniu z wywołaniem statycznej metody fabrycznej? Wydaje mi się, że wywołanie konstruktora w łańcuchu jest semantycznie metodą instancji, podczas gdy sekwencja „utwórz obiekt zjednostkowany i wywołaj jego konstruktor” jest semantycznie równoważna wywołaniu metody statycznej fabryki.
supercat
9

Masz dwa pytania: kiedy należy zadzwonić do getInstance()metody, i kiedy należy utworzyć jeden?

Jeśli decydując czy zadzwonićgetInstance() metoda, to proste. Wystarczy przeczytać dokumentację klasy, aby dowiedzieć się, kiedy należy to wywołać. Na przykład, NumberFormatprzewiduje konstruktora i w getInstance()sposób; getInstance()metoda daje zlokalizowane NumberFormat. Z Calendardrugiej strony konstruktor jest chroniony. Ty masz zadzwonić getInstance(), aby uzyskać jeden.

Jeśli jesteś podejmowaniu decyzji, czy do tworzenia się getInstance()metodę, trzeba zdecydować, co chcesz osiągnąć. Albo nie chcesz, aby ludzie dzwonili do twojego konstruktora (tworzysz singleton lub fabrykę ), albo nie masz nic przeciwko (jak NumberFormatpowyżej, gdzie inicjują niektóre obiekty dla wygody wywołującego).


Krótko mówiąc? Nie martw się o tworzenie getInstance()metod we własnym kodzie. Jeśli nadejdzie czas, kiedy będą przydatne, będziesz wiedział. A w ogóle, jeśli można wywołać konstruktora klasy jest, jesteś prawdopodobnie powinien tego robić, nawet jeśli klasa udostępnia getInstance()metody.

Chris B.
źródło
7

Zastosowania metod getInstance:

Ale przez większość czasu twój obiekt będzie prostym POJO, a użycie publicznych konstruktorów jest najbardziej praktycznym i oczywistym rozwiązaniem.

U1: getInstance z innej klasy

Aby zwrócić instancję innej klasy:

public class FooFactory {
    public static Foo getInstance() {
        return new Foo();
    }
}

NumberFormat.getInstancemetody robią to, ponieważ w rzeczywistości zwracają wystąpienia DecimalFormat.

U2: Problemy singletonów

Wzorzec singleton ogranicza wiele zalet programowania obiektowego. Singletony generalnie mają prywatne konstruktory, dlatego nie można ich rozszerzać. Ponieważ będziesz uzyskiwać do niego dostęp za pomocą metody getInstance i nie będziesz odnosić się do żadnego interfejsu, nie będziesz mógł zamienić go na inną implementację.

krock
źródło
w przypadku wzorca fabrycznego inna nazwa metody, taka jak `` createInstance '' lub `` buildInstance '', byłaby znacznie lepsza, ale nadal możliwy przypadek tego, czego może chcieć pytający (brzmi nadal trochę mglisto i jak praca domowa dla mnie)
jdehaan
6

Jeśli możesz użyć obu, to brzmi to jak źle zaimplementowany wzorzec singletona .

Użyj drugiej opcji, jeśli zamierzasz mieć tylko jedną instancję klasy w swoim systemie i ustaw konstruktor jako prywatny.

Użyj pierwszego, aby umożliwić zbudowanie kilku obiektów tej klasy.

ALE nie dawaj swojej klasie obu możliwości.

Uważaj, aby nie nadużywać singletonów, używaj ich tylko wtedy, gdy naprawdę ma istnieć tylko jedna instancja w systemie, w przeciwnym razie ograniczysz możliwości ponownego wykorzystania swojej klasy w innych projektach. Możliwość wywoływania getInstance z dowolnego miejsca w projekcie brzmi interesująco, ale nie jest jasne, kto faktycznie jest właścicielem tej instancji: nikt i / lub wszyscy. Jeśli masz dużo singletonów w projekcie, możesz się założyć, że system jest źle zaprojektowany (zazwyczaj). Singletonów należy używać ostrożnie, obowiązują te same rady, co w przypadku zmiennych globalnych.

jdehaan
źródło
Zaktualizowałem pytanie, było trochę zagmatwane, przepraszam
zengr
4
------> „Uważaj, aby nie
nadużywać
1

Jednym z przypadków, w których zawsze wolę statyczną fabrykę od zwykłego konstruktora, jest sytuacja, gdy wiem, że budowa obiektu będzie powolna. Wykonuję prostą inicjalizację na konstruktorze, ale jeśli muszę stworzyć coś ciężkiego, użyję metody statycznej i udokumentuję zachowanie.

Guu
źródło
0

Singletony są złe. Problemy, które widziałem, nie dotyczą ponownego wykorzystania lub rozszerzalności systemu (chociaż mogłem zobaczyć, jak to może się zdarzyć), a tym bardziej, że nie mogę policzyć, ile razy widziałem niejasne błędy w system, który powstaje z singletonów.

Jeśli potrzebujesz użyć singletona, upewnij się, że jego zakres jest bardzo wąski, tj. Rozsądnie ogranicz liczbę innych obiektów w systemie, które o nim wiedzą.

rupjones
źródło