Quando è una buona idea usare i metodi factory all'interno di un oggetto anziché in una classe Factory?
Quando è una buona idea usare i metodi factory all'interno di un oggetto anziché in una classe Factory?
Risposte:
Mi piace pensare al design dei pattens in termini di lezioni come "persone", e gli schemi sono i modi in cui le persone si parlano.
Quindi, per me il modello di fabbrica è come un'agenzia di collocamento. Hai qualcuno che avrà bisogno di un numero variabile di lavoratori. Questa persona può conoscere alcune informazioni di cui ha bisogno nelle persone che assumono, ma il gioco è fatto.
Quindi, quando hanno bisogno di un nuovo dipendente, chiamano l'agenzia di collocamento e dicono loro di cosa hanno bisogno. Ora, per assumere effettivamente qualcuno, devi conoscere un sacco di cose - benefici, verifica dell'ammissibilità, ecc. Ma la persona che assume non ha bisogno di sapere nulla di tutto ciò - l'agenzia di assunzione gestisce tutto ciò.
Allo stesso modo, l'utilizzo di una Factory consente al consumatore di creare nuovi oggetti senza dover conoscere i dettagli di come vengono creati o quali sono le loro dipendenze: devono solo fornire le informazioni che desiderano effettivamente.
public interface IThingFactory
{
Thing GetThing(string theString);
}
public class ThingFactory : IThingFactory
{
public Thing GetThing(string theString)
{
return new Thing(theString, firstDependency, secondDependency);
}
}
Quindi, ora il consumatore di ThingFactory può ottenere una cosa, senza dover conoscere le dipendenze della cosa, ad eccezione dei dati di stringa che provengono dal consumatore.
within an object instead of a Factory class
. Penso che intendesse lo scenario in cui rendi privato il ctor e usi un metodo statico per creare un'istanza della classe (creare un oggetto). Ma per seguire questo esempio bisogna prima istanziare la ThingFactory
classe per ottenere Thing
oggetti, il che rende questo Factory class
in effetti.
I metodi di fabbrica dovrebbero essere considerati un'alternativa ai costruttori, soprattutto quando i costruttori non sono abbastanza espressivi, vale a dire.
class Foo{
public Foo(bool withBar);
}
non è espressivo come:
class Foo{
public static Foo withBar();
public static Foo withoutBar();
}
Le classi di fabbrica sono utili quando è necessario un processo complicato per la costruzione dell'oggetto, quando la costruzione richiede una dipendenza che non si desidera per la classe effettiva, quando è necessario costruire oggetti diversi ecc.
Una situazione in cui trovo personalmente diverse classi Factory per dare un senso è quando l'oggetto finale che stai cercando di creare si basa su molti altri oggetti. Ad esempio, in PHP: Supponiamo di avere un House
oggetto, che a sua volta ha un Kitchen
e un LivingRoom
oggetto, e l' LivingRoom
oggetto ha anche un TV
oggetto all'interno.
Il metodo più semplice per raggiungere questo obiettivo è far sì che ogni oggetto crei i propri figli sul loro metodo di costruzione, ma se le proprietà sono relativamente nidificate, quando la House
creazione non riesce, probabilmente passerai un po 'di tempo a cercare di isolare esattamente ciò che sta fallendo.
L'alternativa è fare quanto segue (iniezione di dipendenza, se ti piace il termine elegante):
$TVObj = new TV($param1, $param2, $param3);
$LivingroomObj = new LivingRoom($TVObj, $param1, $param2);
$KitchenroomObj = new Kitchen($param1, $param2);
$HouseObj = new House($LivingroomObj, $KitchenroomObj);
Qui se il processo di creazione di un House
fallisce c'è solo un posto dove cercare, ma dover usare questo pezzo ogni volta che si desidera un nuovo House
è tutt'altro che conveniente. Inserisci le fabbriche:
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();
Grazie alla fabbrica qui il processo di creazione di un House
è astratto (in quanto non è necessario creare e impostare ogni singola dipendenza quando si desidera solo creare un House
) e allo stesso tempo centralizzato che ne facilita la manutenzione. Esistono altri motivi per cui l'utilizzo di Fabbriche separate può essere utile (ad esempio testabilità), ma trovo questo caso d'uso specifico per illustrare come le classi Factory possono essere utili.
HouseFactory
classe?
create
metodo. Ad esempio, se hai House
sempre lo stesso tipo, LivingRoom
allora ha senso avere i suoi parametri hardcoded nella classe factory piuttosto che passare come argomenti. Oppure potresti voler fornire un type
argomento al tuo HouseFactory::create
metodo se hai alcuni tipi di se LivingRoom
hai un interruttore con i parametri hardcoded per ogni tipo.
È importante differenziare chiaramente l'idea alla base del metodo factory o factory. Entrambi hanno lo scopo di affrontare diversi tipi di problemi di creazione di oggetti reciprocamente esclusivi.
Precisiamo il "metodo di fabbrica":
La prima cosa è che, quando si sviluppano librerie o API che a loro volta verranno utilizzate per un ulteriore sviluppo dell'applicazione, il metodo factory è una delle migliori selezioni per il modello di creazione. Motivo dietro; Sappiamo che quando creare un oggetto con le funzionalità richieste ma il tipo di oggetto rimarrà indeciso o verrà deciso di passare parametri dinamici .
Ora il punto è che si può ottenere approssimativamente lo stesso utilizzando lo stesso modello di fabbrica, ma un enorme svantaggio introdurrà nel sistema se il modello di fabbrica verrà utilizzato per il problema sopra evidenziato, è che la tua logica di gettare oggetti diversi (oggetti delle sottoclassi) essere specifici per alcune condizioni aziendali, quindi in futuro quando è necessario estendere le funzionalità della libreria per altre piattaforme (in termini più tecnici, è necessario aggiungere più sottoclassi di interfaccia di base o classe astratta in modo che factory restituisca tali oggetti anche oltre a quello esistente sulla base di alcuni parametri dinamici) quindi ogni volta che è necessario modificare (estendere) la logica della classe di fabbrica che sarà un'operazione costosa e non buona dal punto di vista del design. Dall'altro lato, se "metodo di fabbrica"
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();
}
}
È consigliabile utilizzare i metodi di fabbrica all'interno dell'oggetto quando:
È una buona idea utilizzare la classe di fabbrica astratta quando:
UML da
Prodotto: definisce un'interfaccia degli oggetti creati dal metodo Factory.
ConcreteProduct: implementa l'interfaccia del prodotto
Creatore: dichiara il metodo Factory
ConcreateCreator: implementa il metodo Factory per restituire un'istanza di ConcreteProduct
Dichiarazione del problema: crea una Factory of Games utilizzando Factory Methods, che definisce l'interfaccia di gioco.
Snippet di codice:
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");
}
}
}
produzione:
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
Questo esempio mostra una Factory
classe implementando a FactoryMethod
.
Game
è l'interfaccia per tutti i tipi di giochi. Definisce un metodo complesso:createGame()
Chess, Ludo, Checkers
sono diverse varianti di giochi, che forniscono implementazione a createGame()
public Game getGame(String gameName)
è FactoryMethod
in IGameFactory
classe
GameFactory
crea diversi tipi di giochi nel costruttore. Implementa il IGameFactory
metodo di fabbrica.
game Il nome viene passato come argomento della riga di comando a NotStaticFactoryDemo
getGame
in GameFactory
accetta un nome del gioco e torna corrispondenti Game
oggetto.
Fabbrica:
Crea oggetti senza esporre la logica di istanza al client.
FactoryMethod
Definire un'interfaccia per la creazione di un oggetto, ma lasciare che le sottoclassi decidano quale classe creare un'istanza. Il metodo Factory consente a una classe di rinviare l'istanza alle sottoclassi
Caso d'uso:
Quando usare: Client
non sa quali classi concrete sarà necessario creare in fase di esecuzione, ma vuole solo ottenere una classe che farà il lavoro.
getArea()
non è un metodo factory a tutti .
È davvero una questione di gusti. Le classi di fabbrica possono essere astratte / interfacciate se necessario, mentre i metodi di fabbrica sono più leggeri (e tendono anche a essere testabili, poiché non hanno un tipo definito, ma richiedono un punto di registrazione ben noto, simile a un servizio localizzatore ma per individuare i metodi di fabbrica).
Le classi di fabbrica sono utili quando il tipo di oggetto che restituiscono ha un costruttore privato, quando classi di fabbrica diverse impostano proprietà diverse sull'oggetto di ritorno o quando un tipo di fabbrica specifico è accoppiato al suo tipo di calcestruzzo di ritorno.
WCF utilizza le classi ServiceHostFactory per recuperare gli oggetti ServiceHost in diverse situazioni. ServiceHostFactory standard viene utilizzato da IIS per recuperare le istanze ServiceHost per i file .svc , ma un WebScriptServiceHostFactory viene utilizzato per i servizi che restituiscono serializzazioni ai client JavaScript. ADO.NET Data Services ha il proprio DataServiceHostFactory speciale e ASP.NET ha il suo ApplicationServicesHostFactory poiché i suoi servizi hanno costruttori privati.
Se hai solo una classe che consuma la factory, puoi semplicemente usare un metodo factory all'interno di quella classe.
Prendi in considerazione uno scenario in cui devi progettare un ordine e una classe cliente. Per semplicità e requisiti iniziali, non sentite la necessità della fabbrica per la classe Order e riempite la vostra applicazione con molte dichiarazioni "new Order ()". Le cose stanno funzionando bene.
Ora emerge un nuovo requisito secondo cui l'oggetto Ordine non può essere istanziato senza l'associazione del cliente (nuova dipendenza). Ora hai le seguenti considerazioni.
1- Si crea un sovraccarico del costruttore che funzionerà solo per le nuove implementazioni. (Non accettabile). 2- Si cambiano le firme dell'Ordine () e si cambia ogni singolo invito. (Non è una buona pratica e un vero dolore).
Invece se hai creato una fabbrica per la classe di ordine devi solo cambiare una riga di codice e sei a posto. Suggerisco la classe Factory per quasi tutte le associazioni aggregate. Spero che aiuti.
se si desidera creare un oggetto diverso in termini di utilizzo. È utile.
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;
}
}
Qualsiasi classe che rinvia la creazione dell'oggetto alla sua sottoclasse per l'oggetto con cui deve lavorare può essere vista come un esempio di modello Factory.
Ho citato in dettaglio in un'altra risposta a https://stackoverflow.com/a/49110001/504133
Penso che dipenderà dal grado di accoppiamento lento che vuoi portare al tuo codice.
Il metodo di fabbrica disaccoppia le cose molto bene ma la classe di fabbrica no.
In altre parole, è più facile cambiare le cose se si utilizza il metodo factory che se si utilizza una fabbrica semplice (nota come classe factory).
Guarda in questo esempio: https://connected2know.com/programming/java-factory-pattern/ . Ora, immagina di voler portare un nuovo animale. Nella classe Factory è necessario modificare Factory ma nel metodo factory, no, è sufficiente aggiungere una nuova sottoclasse.
Le classi di fabbrica sono più pesanti, ma offrono alcuni vantaggi. Nei casi in cui è necessario creare oggetti da più origini dati non elaborate, consentono di incapsulare solo la logica di costruzione (e forse l'aggregazione dei dati) in un unico posto. Lì può essere testato in astratto senza preoccuparsi dell'interfaccia dell'oggetto.
Ho trovato questo un modello utile, in particolare dove non sono in grado di sostituire e inadeguato ORM e voglio creare un'istanza efficiente di molti oggetti dai join di tabelle DB o dalle procedure memorizzate.
Ho paragonato le fabbriche al concetto di biblioteche. Ad esempio puoi avere una libreria per lavorare con i numeri e un'altra per lavorare con le forme. È possibile memorizzare le funzioni di queste librerie in directory con nome logico come Numbers
oShapes
. Questi sono tipi generici che potrebbero includere numeri interi, float, dobuli, lunghe o rettangoli, cerchi, triangoli, pentagoni nel caso di forme.
Il petter di fabbrica utilizza polimorfismo, iniezione di dipendenza e inversione di controllo.
Lo scopo dichiarato dei modelli di fabbrica è: Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
Diciamo quindi che stai costruendo un sistema operativo o un framework e stai costruendo tutti i componenti discreti.
Ecco un semplice esempio del concetto di Factory Pattern in PHP. Potrei non essere al 100% su tutto, ma è destinato a servire da semplice esempio. Non sono un esperto.
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 );
mi sta dando semplicemente dicendo new Float(12.5);
se so di aver bisogno di un Float
? Questo è ciò che non capisco delle fabbriche ... qual è il punto?