W niektórych moich kodach mam statyczną fabrykę podobną do tej:
public class SomeFactory {
// Static class
private SomeFactory() {...}
public static Foo createFoo() {...}
public static Foo createFooerFoo() {...}
}
Podczas przeglądu kodu zaproponowano, aby był to singleton i został wstrzyknięty. Powinno to wyglądać tak:
public class SomeFactory {
public SomeFactory() {}
public Foo createFoo() {...}
public Foo createFooerFoo() {...}
}
Kilka rzeczy do podkreślenia:
- Obie fabryki są bezpaństwowcami.
- Jedyną różnicą między metodami są ich zakresy (instancja vs. statyczność). Wdrożenia są takie same.
- Foo to fasola, która nie ma interfejsu.
Argumenty, które poparłem, aby stać się statyczne to:
- Klasa jest bezstanowa, dlatego nie trzeba jej tworzyć
- Wydaje się bardziej naturalne, że można wywołać metodę statyczną niż tworzyć instancję fabryki
Argumenty przemawiające za fabryką jako singletonem były:
- Dobrze jest wstrzykiwać wszystko
- Pomimo bezpaństwowości fabryki testowanie jest łatwiejsze dzięki iniekcji (łatwe do wyśmiewania)
- Należy wyśmiewać go podczas testowania konsumenta
Mam kilka poważnych problemów z podejściem singleton, ponieważ wydaje się sugerować, że żadna metoda nigdy nie powinna być statyczna. Wydaje się również sugerować, że narzędzia takie jak StringUtils
powinny być pakowane i wstrzykiwane, co wydaje się głupie. Wreszcie oznacza to, że w pewnym momencie będę musiał kpić z fabryki, co nie wydaje się właściwe. Nie mogę wymyślić, kiedy będę musiał kpić z fabryki.
Co myśli społeczność? Chociaż nie podoba mi się podejście singleton, nie wydaje mi się, aby miałam przeciwko temu strasznie silny argument.
źródło
DateTime
iFile
są niezwykle trudne do przetestowania z dokładnie tych samych powodów. Jeśli masz na przykład klasę, która ustawiaCreated
datęDateTime.Now
w konstruktorze, jak przejść do tworzenia testu jednostkowego z dwoma z tych obiektów, które zostały utworzone w odstępie 5 minut? Co powiesz na lata? Naprawdę nie możesz tego zrobić (bez dużo pracy).private
konstruktora igetInstance()
metody? Przepraszamy, niepoprawny próbnik nitów!Odpowiedzi:
Dlaczego oddzielisz swoją fabrykę od typu obiektu, który tworzy?
Oto, co Joshua Bloch opisuje jako swój przedmiot 1 na stronie 5 swojej książki „Skuteczna Java”. Java jest wystarczająco gadatliwa bez dodawania dodatkowych klas i singletonów i tak dalej. Istnieją przypadki, w których oddzielna klasa fabryczna ma sens, ale jest ich niewiele.
Parafrazując Przedmiot 1 Joshua Blocha, w przeciwieństwie do konstruktorów, statyczne metody fabryczne mogą:
Niedogodności:
Naprawdę, powinieneś zanieść książkę Blocha do recenzenta kodu i sprawdzić, czy oboje możecie zgodzić się na styl Blocha.
źródło
public static IFoo of(...) { return new Foo(...); }
Jaki jest problem? Bloch wymienia satysfakcję z twojej troski jako jedną z zalet tego projektu (drugi punkt powyżej). Nie mogę zrozumieć twojego komentarza.Oto kilka problemów ze statyką w Javie:
metody statyczne nie działają zgodnie z „regułami” OOP. Nie możesz zmusić klasy do wdrożenia określonych metod statycznych (o ile mi wiadomo). Dlatego nie można mieć wielu klas implementujących ten sam zestaw metod statycznych z tymi samymi podpisami. Więc utkniesz z jednym z tych działań. Nie można też tak naprawdę podklasować klasy statycznej. Więc nie ma polimorfizmu itp.
ponieważ metody nie są obiektami, metod statycznych nie można przekazywać i nie można ich używać (bez mnóstwa kodowania na płycie głównej), z wyjątkiem kodu, który jest z nimi połączony na stałe - co sprawia, że kod klienta jest wysoce sprzężony. (Szczerze mówiąc, nie jest to wyłącznie wina statyki).
pola statyki są dość podobne do globałów
Wspomniałeś:
Dlatego ciekawi mnie pytanie:
StringUtils
jest poprawnie zaprojektowany i wdrożony?źródło
Foo f = createFoo();
, niż mieć nazwaną instancję. Sama instancja nie zapewnia żadnego narzędzia. (2) Tak mi się wydaje. Został zaprojektowany, aby przezwyciężyć fakt, że wiele z tych metod nie istnieje wString
klasie. Zastąpienie czegoś tak fundamentalnego, jakString
nie jest opcją, więc utils są na dobrej drodze. (kont.)Integer
. Są bardzo proste, mają bardzo małą logikę i służą jedynie wygodzie (np. Nie ma innego powodu, by je mieć). Główną częścią mojego argumentu jest to, że nie polegają na żadnym stanie - nie ma powodu, aby je izolować podczas testów. Ludzie nie kpią zStringUtils
testów - dlaczego mieliby kpić z tej prostej fabryki wygody?StringUtils
ponieważ istnieje uzasadniona pewność, że metody tam nie wywołują żadnych skutków ubocznych.StringUtils
zwraca jakiś wynik bez zmiany danych wejściowych, moja fabryka również niczego nie modyfikuje.Myślę, że tworzona aplikacja musi być dość nieskomplikowana, w przeciwnym razie jesteś stosunkowo wcześnie w jej cyklu życia. Jedynym innym scenariuszem, jaki mogę wymyślić, w którym nie mielibyście już problemów ze statystyką, jest to, że udało się ograniczyć inne części kodu, które znają fabrykę do jednego lub dwóch miejsc.
Wyobraź sobie, że Twoja aplikacja ewoluuje, a teraz w niektórych przypadkach musisz stworzyć
FooBar
(podklasę Foo). Teraz musisz przejrzeć wszystkie miejsca w kodzie, które wiedzą o tobieSomeFactory
i napisać logikę, która patrzysomeCondition
i wywołujeSomeFactory
lubSomeOtherFactory
. W rzeczywistości, patrząc na twój przykładowy kod, jest gorzej. Planujesz po prostu dodawać metodę po metodzie do tworzenia różnych Foos, a cały kod klienta musi sprawdzić warunek przed ustaleniem, który Foo należy wykonać. Co oznacza, że wszyscy klienci muszą mieć dogłębną wiedzę o tym, jak działa Twoja Fabryka i wszyscy muszą się zmieniać, ilekroć nowe Foo jest potrzebne (lub usunięte!).Teraz wyobraź sobie, czy właśnie stworzyłeś coś
IFooFactory
, co tworzyIFoo
. Teraz tworzenie innej podklasy lub nawet zupełnie innej implementacji jest dziecinnie proste - wystarczy wprowadzić odpowiedni IFooFactory wyżej w łańcuchu, a następnie, gdy klient go dostanie, zadzwonigimmeAFoo()
i dostanie odpowiednią foo, ponieważ ma odpowiednią fabrykę .Być może zastanawiasz się, jak to wygląda w kodzie produkcyjnym. Aby dać wam przykład z bazy kodu, nad którą pracuję, która zaczyna się wyrównywać po nieco ponad roku rozwijania projektów, mam kilka fabryk, które są używane do tworzenia obiektów danych pytań (są one trochę jak modele prezentacji ). Mam
QuestionFactoryMap
w zasadzie hash do wyszukiwania, której fabryki pytań użyć, kiedy. Aby stworzyć aplikację zadającą różne pytania, umieściłem na mapie różne fabryki.Pytania mogą być prezentowane w instrukcjach lub ćwiczeniach (które oba wdrażają
IContentBlock
), więc każdyInstructionsFactory
lubExerciseFactory
dostanie kopię QuestionFactoryMap. Następnie przekazywane są różne fabryki instrukcji i ćwiczeń,ContentBlockBuilder
które ponownie zachowują skrót, z którego można korzystać. A potem, gdy XML jest załadowany, ContentBlockBuilder przywołuje Fabrykę Ćwiczeń lub Instrukcji z hasza i prosi o tocreateContent()
, a następnie Fabryka deleguje budowę pytań do wszystkiego, co wyciągnie ze swojej wewnętrznej QuestionFactoryMap w oparciu o jest w każdym węźle XML kontra skrót. Niestety , ta rzecz jestBaseQuestion
zamiastIQuestion
, ale to tylko pokazuje, że nawet jeśli jesteś ostrożny w projektowaniu, możesz popełniać błędy, które mogą cię prześladować.Twoje wnętrzności mówią ci, że te stany cię zranią, i na pewno jest w literaturze dużo, aby wspierać jelito. Nigdy nie stracisz, posiadając Zastrzyk Zależności, którego użyjesz tylko do testów jednostkowych (nie znaczy to, że wierzę, że jeśli zbudujesz dobry kod, że nie będziesz go więcej używał), ale statyka może całkowicie zniszczyć twoją zdolność aby szybko i łatwo zmodyfikować kod. Dlaczego więc tam pójść?
źródło
Integer
klasie - prostych rzeczach, takich jak konwersja z ciągu znaków. Właśnie toFoo
robi. MójFoo
nie ma być przedłużany (moja wina, że tego nie powiedziałem), więc nie mam twoich problemów polimorficznych. Rozumiem wartość posiadania fabryk polimorficznych i korzystałem z nich w przeszłości ... Po prostu nie sądzę, aby były one odpowiednie tutaj. Czy możesz sobie wyobrazić, że musisz utworzyć instancję fabryki, aby wykonać proste operacje,Integer
które są obecnie wypierane przez statyczne fabryki?Foo
jednak o wiele prostsze niż wiele klas. To tylko proste POJO.if (this) then {makeThisFoo()} else {makeThatFoo()}
cały kod. Jedną z rzeczy, które zauważyłem w przypadku osób z chronicznie złą architekturą, jest to, że są jak ludzie cierpiący na przewlekły ból. Nie wiedzą, jak to jest nie odczuwać bólu, więc ból, w którym się znajdują, nie wydaje się taki zły.Foo
. Słusznie więc - nigdy nie zadeklarowałem tego jako ostatecznego.Foo
to fasola, której nie należy przedłużać.