Celem wzoru fabrycznego jest de-para jakiś kod typu run-time obiektu to konsumuje:
// This code doesn't need to know that the factory is returning
// an object of type `com.example.parties.SurpriseParty`
AbstractParty myParty = new PartyFactory().create(...);
Korzystanie kod jak tego, PartyFactory
jest wyłącznie odpowiedzialny za ustalenie lub dokładne określenie, jaki typ programu powinien być używany.
Zaspokajasz to świadczenie, przekazując w pełni kwalifikowaną nazwę klasy, której potrzebujesz. Jak to ...
// This code obviously DOES know that the factory is returning
// an object of type `com.example.parties.SurpriseParty`.
// Now only the compiler doesn't know or enforce that relationship.
AbstractParty myParty = new PartyFactory().create("com.example.parties.SurpriseParty");
... każdy różni się od po prostu deklarując myParty
jako typu com.example.parties.SurpriseParty
? Na koniec twój kod jest tak samo sprzężony, ale zrezygnowałeś ze statycznej weryfikacji typu. Oznacza to, że ponosisz mniej niż żadna korzyść, podczas gdy niektóre korzyści z Javy są ściśle pisane. Jeśli usuniesz kod com.example.parties.SurpriseParty
, twój kod się skompiluje, twój IDE nie dostarczy żadnych komunikatów o błędach i nie zdasz sobie sprawy, że istnieje związek między tym kodem a com.example.parties.SurpriseParty
do czasu uruchomienia - to jest złe.
Przynajmniej, radzę ci przynajmniej zmienić ten kod tak argumentem tejże metody jest prosta nazwa klasy, a nie w pełni kwalifikowana nazwa:
// I took the liberty of renaming this class and it's only method
public class MyPartyFactory{
public Party create(String name)
{
//TODO: sanitize `name` - check it contains no `.` characters
Class c = Class.forName("com.example.parties."+name);
// I'm going to take for granted that I don't have to explain how or why `party` shouldn't be an instance variable.
Party party = (PersonalParty)c.newInstance();
return party;
}
}
Następny: to jest złą praktyką użyć Class.forName(...)
? To zależy od tego, czym jest alternatywa, i relacji między argumentami String
a klasami, które zapewni ta fabryka. Jeśli alternatywą jest duży warunek:
if("SurpriseParty".equals(name) {
return new com.example.parties.SurpriseParty();
}
else if("GoodbyeParty".equals(name)) {
return new com.example.parties.GoodbyeParty();
}
else if("PartyOfFive".equals(name)) {
return new com.example.parties.PartyOfFive();
}
else if(/* ... */) {
// ...
}
// etc, etc etc
... to nie jest skalowalne. Ponieważ istnieje wyraźna obserwowalna zależność między nazwami typów wykonawczych tworzonych przez tę fabrykę a wartością argumentu name
, należy rozważyć użycie wartości Class.forName
. W ten sposób Twój obiekt Factory
jest chroniony przed zmianą kodu za każdym razem, gdy dodajesz nowy system Party
.
Zamiast tego można rozważyć użycie wzoru AbstractFactory
. Jeśli kod spożywania wygląda następująco:
AbstractParty sParty = new PartyFactory().create("SurpriseParty");
AbstractParty gbParty = new PartyFactory().create("GoodByeParty");
... gdzie istnieje ograniczona liczba często występujących typów stron, które wnioskowano, należy rozważyć różne metody dla tych różnych typów stron:
public class PartyFactory {
public Party getSurpriseParty() { ... }
public Party getGoodByeParty() { ... }
}
...co pozwoli ci wykorzystać statyczne pisanie Java.
To rozwiązanie oznacza jednak, że za każdym razem, gdy dodajesz nowy typ Party
, musisz zmienić obiekt fabryczny - tak, czy rozwiązanie odblaskowe, czy AbstractFactory
jest lepszym rozwiązaniem, naprawdę zależy od tego, jak często i jak szybko Będą dodawać typy Party
. Nowy typ każdego dnia? Użyj refleksji. Nowy typ imprezy co dekadę? Użyj AbstractFactory
.
Czy jest to przydatny przypadek. Ogólnie rzecz biorąc, rozważałbym refleksję jako moją ostatnią deskę ratunku. Specjalnie do tego celu. –
Co ze strukturami takimi jak JSf2.0, które używa adnotacji? Powinny używać interfejsów API Reflection do tworzenia instancji obiektu w czasie wykonywania? – Ullas
Jeśli chodzi o JSF, to oznacz to jako tak. –