Kiedy dobrym pomysłem jest stosowanie metod fabrycznych w obiekcie zamiast klasy Factory?
Kiedy dobrym pomysłem jest stosowanie metod fabrycznych w obiekcie zamiast klasy Factory?
Odpowiedzi:
Lubię myśleć o wzorach projektowych w kategoriach tego, że moje zajęcia są „ludźmi”, a wzorce są sposobem, w jaki ludzie rozmawiają ze sobą.
Tak więc według mnie wzór fabryczny jest jak biuro pośrednictwa pracy. Masz kogoś, kto będzie potrzebował zmiennej liczby pracowników. Ta osoba może znać pewne informacje na temat zatrudnionych osób, ale to wszystko.
Kiedy potrzebują nowego pracownika, dzwonią do agencji pośrednictwa pracy i mówią im, czego potrzebują. Teraz, aby faktycznie kogoś zatrudnić , musisz wiedzieć wiele rzeczy - korzyści, weryfikację uprawnień itp. Ale osoba zatrudniająca nie musi nic o tym wiedzieć - agencja rekrutacyjna zajmuje się tym wszystkim.
W ten sam sposób korzystanie z fabryki pozwala konsumentowi tworzyć nowe obiekty bez konieczności poznawania szczegółów ich tworzenia ani zależności od nich - muszą jedynie podać informacje, których naprawdę potrzebują.
public interface IThingFactory
{
Thing GetThing(string theString);
}
public class ThingFactory : IThingFactory
{
public Thing GetThing(string theString)
{
return new Thing(theString, firstDependency, secondDependency);
}
}
Tak więc teraz konsument ThingFactory może uzyskać Rzecz bez konieczności znajomości zależności Rzeczy, z wyjątkiem danych łańcuchowych, które pochodzą od konsumenta.
within an object instead of a Factory class
. Myślę, że miał na myśli scenariusz, w którym uczynisz ctor prywatnym i użyjesz metody statycznej, aby utworzyć instancję klasy (stworzyć obiekt). Aby jednak podążać za tym przykładem, należy najpierw utworzyć instancję ThingFactory
klasy, aby uzyskać Thing
obiekty, co powoduje, że działa to Factory class
poprawnie.
Metody fabryczne należy traktować jako alternatywę dla konstruktorów - głównie wtedy, gdy konstruktory nie są wystarczająco ekspresyjne, tj.
class Foo{
public Foo(bool withBar);
}
nie jest tak wyrazisty jak:
class Foo{
public static Foo withBar();
public static Foo withoutBar();
}
Klasy fabryczne są przydatne, gdy potrzebujesz skomplikowanego procesu konstruowania obiektu, gdy konstrukcja wymaga zależności, której nie chcesz dla rzeczywistej klasy, gdy potrzebujesz konstruować różne obiekty itp.
Jedną z sytuacji, w której osobiście uważam, że osobne klasy Factory mają sens, jest to, że ostateczny obiekt, który próbujesz stworzyć, opiera się na kilku innych obiektach. Np. W PHP: Załóżmy, że masz House
obiekt, który z kolei ma a Kitchen
i LivingRoom
obiekt, a LivingRoom
obiekt maTV
obiekt wewnątrz, jak również.
Najprostszym sposobem na osiągnięcie tego jest utworzenie przez każdy obiekt swoich dzieci na podstawie metody konstruowania, ale jeśli właściwości są względnie zagnieżdżone, gdy House
tworzenie się nie powiedzie, prawdopodobnie poświęcisz trochę czasu na wyodrębnienie tego, co nie działa.
Alternatywą jest wykonanie następujących czynności (wstrzyknięcie zależności, jeśli podoba Ci się wymyślny termin):
$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);
Tutaj, jeśli proces tworzenia House
awarii kończy się niepowodzeniem, jest tylko jedno miejsce do patrzenia, ale konieczność korzystania z tego fragmentu za każdym razem, gdy chce się nowego, House
jest daleka od wygody. Wejdź do fabryk:
class HouseFactory {
public function create() {
$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);
return $HouseObj;
}
}
$houseFactory = new HouseFactory();
$HouseObj = $houseFactory->create();
Dzięki fabryce proces tworzenia a House
jest abstrakcyjny (w tym sensie, że nie musisz tworzyć i konfigurować każdej zależności, gdy chcesz tylko utworzyć a House
), a jednocześnie scentralizowany, co ułatwia utrzymanie. Są inne powody, dla których korzystanie z oddzielnych fabryk może być korzystne (np. Testowalność), ale ten konkretny przypadek użycia znajduję, aby najlepiej zilustrować, w jaki sposób klasy fabryczne mogą być przydatne.
HouseFactory
klasie?
create
metody. Np. Jeśli twój House
zawsze będzie miał taki sam rodzaj, LivingRoom
sensowne może być zapisanie jego parametrów w klasie fabrycznej zamiast podania ich jako argumentu. Lub możesz podać type
argument do swojej HouseFactory::create
metody, jeśli masz kilka rodzajów LivingRoom
si i masz przełącznik z parametrami zakodowanymi na stałe dla każdego typu.
Ważne jest, aby wyraźnie rozróżnić pomysł za pomocą metody fabrycznej lub fabrycznej. Oba mają na celu rozwiązywanie wzajemnie wykluczających się różnych problemów związanych z tworzeniem obiektów.
Powiedzmy konkretnie o „metodzie fabrycznej”:
Po pierwsze, kiedy tworzysz bibliotekę lub interfejsy API, które z kolei będą wykorzystywane do dalszego rozwoju aplikacji, wówczas metoda fabryczna jest jednym z najlepszych wyborów wzorca tworzenia. Powód za; Wiemy, że kiedy utworzyć obiekt o wymaganych funkcjach, ale typ obiektu pozostanie niezdecydowany lub zostanie podjęta decyzja o przekazaniu parametrów dynamicznych .
Chodzi o to, że w przybliżeniu to samo można osiągnąć przy użyciu samego wzorca fabrycznego, ale jedna wielka wada wprowadzi się do systemu, jeśli wzorzec fabryczny zostanie użyty do wyżej wymienionego problemu, jest to, że twoja logika tworzenia różnych obiektów (obiektów podklas) będzie bądź specyficzny dla niektórych warunków biznesowych, więc w przyszłości, gdy będziesz musiał rozszerzyć funkcjonalność swojej biblioteki na inne platformy (technicznie rzecz biorąc, musisz dodać więcej podklas podstawowego interfejsu lub klasy abstrakcyjnej, aby fabryka zwróciła te obiekty oprócz istniejącej na podstawie niektórych parametrów dynamicznych), a następnie za każdym razem, gdy trzeba zmienić (rozszerzyć) logikę klasy fabrycznej, co będzie kosztowne, a nie dobre z punktu widzenia projektu. Z drugiej strony, jeśli „metoda fabryczna”
interface Deliverable
{
/*********/
}
abstract class DefaultProducer
{
public void taskToBeDone()
{
Deliverable deliverable = factoryMethodPattern();
}
protected abstract Deliverable factoryMethodPattern();
}
class SpecificDeliverable implements Deliverable
{
/***SPECIFIC TASK CAN BE WRITTEN HERE***/
}
class SpecificProducer extends DefaultProducer
{
protected Deliverable factoryMethodPattern()
{
return new SpecificDeliverable();
}
}
public class MasterApplicationProgram
{
public static void main(String arg[])
{
DefaultProducer defaultProducer = new SpecificProducer();
defaultProducer.taskToBeDone();
}
}
Dobrym pomysłem jest stosowanie metod fabrycznych wewnątrz obiektu, gdy:
Dobrym pomysłem jest użycie abstrakcyjnej klasy fabrycznej, gdy:
UML od
Produkt: Definiuje interfejs obiektów tworzonych przez metodę Factory.
Produkt betonowy: Implementuje interfejs produktu
Twórca: Deklaruje metodę Factory
ConcreateCreator: implementuje metodę Factory w celu zwrócenia instancji ConcreteProduct
Stwierdzenie problemu: Utwórz fabrykę gier za pomocą metod fabrycznych, które definiują interfejs gry.
Fragment kodu:
import java.util.HashMap;
/* Product interface as per UML diagram */
interface Game{
/* createGame is a complex method, which executes a sequence of game steps */
public void createGame();
}
/* ConcreteProduct implementation as per UML diagram */
class Chess implements Game{
public Chess(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Chess game");
System.out.println("Opponents:2");
System.out.println("Define 64 blocks");
System.out.println("Place 16 pieces for White opponent");
System.out.println("Place 16 pieces for Black opponent");
System.out.println("Start Chess game");
System.out.println("---------------------------------------");
}
}
class Checkers implements Game{
public Checkers(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Checkers game");
System.out.println("Opponents:2 or 3 or 4 or 6");
System.out.println("For each opponent, place 10 coins");
System.out.println("Start Checkers game");
System.out.println("---------------------------------------");
}
}
class Ludo implements Game{
public Ludo(){
}
public void createGame(){
System.out.println("---------------------------------------");
System.out.println("Create Ludo game");
System.out.println("Opponents:2 or 3 or 4");
System.out.println("For each opponent, place 4 coins");
System.out.println("Create two dices with numbers from 1-6");
System.out.println("Start Ludo game");
System.out.println("---------------------------------------");
}
}
/* Creator interface as per UML diagram */
interface IGameFactory {
public Game getGame(String gameName);
}
/* ConcreteCreator implementation as per UML diagram */
class GameFactory implements IGameFactory {
HashMap<String,Game> games = new HashMap<String,Game>();
/*
Since Game Creation is complex process, we don't want to create game using new operator every time.
Instead we create Game only once and store it in Factory. When client request a specific game,
Game object is returned from Factory instead of creating new Game on the fly, which is time consuming
*/
public GameFactory(){
games.put(Chess.class.getName(),new Chess());
games.put(Checkers.class.getName(),new Checkers());
games.put(Ludo.class.getName(),new Ludo());
}
public Game getGame(String gameName){
return games.get(gameName);
}
}
public class NonStaticFactoryDemo{
public static void main(String args[]){
if ( args.length < 1){
System.out.println("Usage: java FactoryDemo gameName");
return;
}
GameFactory factory = new GameFactory();
Game game = factory.getGame(args[0]);
if ( game != null ){
game.createGame();
System.out.println("Game="+game.getClass().getName());
}else{
System.out.println(args[0]+ " Game does not exists in factory");
}
}
}
wynik:
java NonStaticFactoryDemo Chess
---------------------------------------
Create Chess game
Opponents:2
Define 64 blocks
Place 16 pieces for White opponent
Place 16 pieces for Black opponent
Start Chess game
---------------------------------------
Game=Chess
Ten przykład pokazuje Factory
klasę, implementując FactoryMethod
.
Game
jest interfejsem dla wszystkich rodzajów gier. Definiuje złożoną metodę:createGame()
Chess, Ludo, Checkers
są różne warianty gier, które zapewniają implementację do createGame()
public Game getGame(String gameName)
jest FactoryMethod
w IGameFactory
klasie
GameFactory
wstępnie tworzy różne typy gier w konstruktorze. Implementuje IGameFactory
metodę fabryczną.
Nazwa gry jest przekazywana jako argument wiersza poleceń do NotStaticFactoryDemo
getGame
in GameFactory
akceptuje nazwę gry i zwraca odpowiedni Game
obiekt.
Fabryka:
Tworzy obiekty bez ujawniania klientowi logiki tworzenia instancji.
FactoryMethod
Zdefiniuj interfejs do tworzenia obiektu, ale pozwól, aby podklasy zdecydowały, którą klasę utworzyć. Metoda Factory pozwala klasie odroczyć tworzenie instancji do podklas
Przypadek użycia:
Kiedy używać: Client
nie wie, jakie konkretne klasy będzie trzeba utworzyć w czasie wykonywania, ale chce tylko uzyskać klasę, która wykona zadanie.
getArea()
Nie jest to metoda fabryki w ogóle .
To naprawdę kwestia gustu. Klasy fabryczne można w razie potrzeby wyodrębnić / powiązać, podczas gdy metody fabryczne są lżejsze (a także zwykle są testowalne, ponieważ nie mają określonego typu, ale będą wymagały znanego punktu rejestracji, podobnego do usługi lokalizator, ale do lokalizowania metod fabrycznych).
Klasy fabryczne są przydatne, gdy zwracany typ obiektu ma prywatny konstruktor, gdy różne klasy fabryczne ustawiają różne właściwości zwracanego obiektu lub gdy określony typ fabryki jest sprzężony z jego zwracanym typem konkretnym.
WCF używa klas ServiceHostFactory do pobierania obiektów ServiceHost w różnych sytuacjach. Standardowy ServiceHostFactory jest używany przez IIS do pobierania instancji ServiceHost dla .svc plików , ale WebScriptServiceHostFactory jest używany dla usług, które zwracają serializacje do klientów JavaScript. Usługi danych ADO.NET mają swoje własne DataServiceHostFactory, a ASP.NET ma ApplicationServicesHostFactory, ponieważ ich usługi mają prywatnych konstruktorów.
Jeśli masz tylko jedną klasę, która pochłania fabrykę, możesz po prostu użyć metody fabrycznej w tej klasie.
Rozważ scenariusz, w którym musisz zaprojektować klasę zamówienia i klienta. Dla uproszczenia i wstępnych wymagań nie czujesz potrzeby fabryki dla klasy Order i wypełnij swoją aplikację wieloma oświadczeniami „new Order ()”. Wszystko działa dobrze.
Teraz pojawia się nowe wymaganie, że obiekt zamówienia nie może zostać utworzony bez skojarzenia klienta (nowa zależność). Teraz masz następujące uwagi.
1- Tworzysz przeciążenie konstruktora, które będzie działać tylko w przypadku nowych implementacji. (Nie do zaakceptowania). 2- Zmieniasz podpisy Order () i zmieniasz każde wywołanie. (Nie jest to dobra praktyka i prawdziwy ból).
Zamiast tego Jeśli utworzyłeś fabrykę dla klasy zamówień, musisz zmienić tylko jeden wiersz kodu i możesz zacząć. Sugeruję klasę fabryczną dla prawie każdego skojarzenia. Mam nadzieję, że to pomaga.
jeśli chcesz stworzyć inny obiekt pod względem użytkowania. To jest użyteczne.
public class factoryMethodPattern {
static String planName = "COMMERCIALPLAN";
static int units = 3;
public static void main(String args[]) {
GetPlanFactory planFactory = new GetPlanFactory();
Plan p = planFactory.getPlan(planName);
System.out.print("Bill amount for " + planName + " of " + units
+ " units is: ");
p.getRate();
p.calculateBill(units);
}
}
abstract class Plan {
protected double rate;
abstract void getRate();
public void calculateBill(int units) {
System.out.println(units * rate);
}
}
class DomesticPlan extends Plan {
// @override
public void getRate() {
rate = 3.50;
}
}
class CommercialPlan extends Plan {
// @override
public void getRate() {
rate = 7.50;
}
}
class InstitutionalPlan extends Plan {
// @override
public void getRate() {
rate = 5.50;
}
}
class GetPlanFactory {
// use getPlan method to get object of type Plan
public Plan getPlan(String planType) {
if (planType == null) {
return null;
}
if (planType.equalsIgnoreCase("DOMESTICPLAN")) {
return new DomesticPlan();
} else if (planType.equalsIgnoreCase("COMMERCIALPLAN")) {
return new CommercialPlan();
} else if (planType.equalsIgnoreCase("INSTITUTIONALPLAN")) {
return new InstitutionalPlan();
}
return null;
}
}
Każda klasa odraczająca tworzenie obiektu do swojej podklasy dla obiektu, z którym musi pracować, może być postrzegana jako przykład wzorca fabrycznego.
Wspominałem szczegółowo w innej odpowiedzi na https://stackoverflow.com/a/49110001/504133
Myślę, że będzie to zależeć od luźnego stopnia sprzężenia, który chcesz wprowadzić do swojego kodu.
Metoda fabryczna oddziela rzeczy bardzo dobrze, ale klasa fabryczna nr.
Innymi słowy, łatwiej jest zmieniać rzeczy, jeśli używasz metody fabrycznej niż w przypadku prostej fabryki (znanej jako klasa fabryczna).
Spójrz na ten przykład: https://connected2know.com/programming/java-factory-pattern/ . Teraz wyobraź sobie, że chcesz przynieść nowe zwierzę. W klasie Factory musisz zmienić fabrykę, ale w metodzie fabrycznej nie, musisz tylko dodać nową podklasę.
Klasy fabryczne są bardziej ciężkie, ale dają pewne korzyści. W przypadkach, gdy musisz zbudować swoje obiekty z wielu surowych źródeł danych, pozwalają one na enkapsulację tylko logiki budynku (i być może agregacji danych) w jednym miejscu. Tam można go przetestować abstrakcyjnie, nie zajmując się interfejsem obiektowym.
Uznałem to za użyteczny wzorzec, szczególnie tam, gdzie nie jestem w stanie zastąpić i nieadekwatnej ORM i chcę wydajnie tworzyć instancje wielu obiektów z połączeń DB lub procedur przechowywanych.
Porównuję fabryki do koncepcji bibliotek. Na przykład możesz mieć bibliotekę do pracy z liczbami i inną do pracy z kształtami. Możesz przechowywać funkcje tych bibliotek w logicznie nazwanych katalogach jako Numbers
lubShapes
. Są to typy ogólne, które mogą obejmować liczby całkowite, liczby zmiennoprzecinkowe, dobule, długie lub prostokąty, koła, trójkąty, pięciokąty w przypadku kształtów.
Petter w fabryce wykorzystuje polimorfizm, wstrzykiwanie zależności i odwracanie kontroli.
Deklarowanym celem Wzorów Fabrycznych jest: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
Powiedzmy, że budujesz system operacyjny lub platformę i budujesz wszystkie dyskretne komponenty.
Oto prosty przykład koncepcji wzorca fabrycznego w PHP. Może nie jestem w 100% na tym wszystkim, ale ma to służyć jako prosty przykład. Nie jestem ekspertem.
class NumbersFactory {
public static function makeNumber( $type, $number ) {
$numObject = null;
$number = null;
switch( $type ) {
case 'float':
$numObject = new Float( $number );
break;
case 'integer':
$numObject = new Integer( $number );
break;
case 'short':
$numObject = new Short( $number );
break;
case 'double':
$numObject = new Double( $number );
break;
case 'long':
$numObject = new Long( $number );
break;
default:
$numObject = new Integer( $number );
break;
}
return $numObject;
}
}
/* Numbers interface */
abstract class Number {
protected $number;
public function __construct( $number ) {
$this->number = $number;
}
abstract public function add();
abstract public function subtract();
abstract public function multiply();
abstract public function divide();
}
/* Float Implementation */
class Float extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Integer Implementation */
class Integer extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Short Implementation */
class Short extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Double Implementation */
class Double extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
/* Long Implementation */
class Long extends Number {
public function add() {
// implementation goes here
}
public function subtract() {
// implementation goes here
}
public function multiply() {
// implementation goes here
}
public function divide() {
// implementation goes here
}
}
$number = NumbersFactory::makeNumber( 'float', 12.5 );
NumbersFactory::makeNumber( 'float', 12.5 );
sprawia, że mówię, new Float(12.5);
jeśli wiem, że potrzebuję Float
? Tego nie rozumiem w fabrykach ... o co chodzi?