Il modello di fabbrica viola il principio aperto / chiuso?


14

Perché questo ShapeFactory utilizza istruzioni condizionali per determinare quale oggetto creare un'istanza. Non dobbiamo modificare ShapeFactory se vogliamo aggiungere altre classi in futuro? Perché questo non viola il principio aperto chiuso?

Design pattern di fabbrica

Design ShapeFactory


2
A quale "modello di fabbrica" ​​ti riferisci esattamente? In generale, una factory è qualsiasi oggetto o metodo che serve per creare un'istanza di un oggetto. Quindi ci sono variazioni specifiche di questa idea generale come il modello astratto di fabbrica, in cui ogni istanza di fabbrica rappresenta una tavolozza di scelte specifica - di solito gestita tramite sottoclasse piuttosto che condizionali.
am


3
Grazie per queste informazioni, questo chiarisce molto. Questo è definitivamente un esempio di modello di fabbrica, ma non del modello di fabbrica astratto comunemente associato ai modelli di fabbrica. Il codice in quell'articolo è abbastanza discutibile e non mi aspetterei di vedere nulla del genere in codice reale.
Amon,

@ArmonSafai: Stai collegando molto questo post sul blog, ma non stai davvero spiegando perché. Siamo tutti in qualche modo ignoranti del modello? Abbiamo anche Google, proprio come te.
Robert Harvey,

1
@RobertHarvey Sto collegando questo post del blog per mostrare come il modello di fabbrica in quella pagina sta usando i condizionali
Armon Safai

Risposte:


20

La saggezza convenzionale orientata agli oggetti è di evitare ifaffermazioni e sostituirle con invio dinamico di metodi scavalcati in sottoclassi di una classe astratta. Fin qui tutto bene.

Ma il punto del modello di fabbrica è di sollevarti dal dover conoscere le singole sottoclassi e lavorare solo con la superclasse astratta . L'idea è che la fabbrica sappia meglio di te quale classe specifica creare un'istanza, e starai meglio lavorando solo con i metodi pubblicati dalla super classe. Questo è spesso vero e un modello prezioso.

Pertanto, non è possibile che la scrittura di una classe factory possa rinunciare alle ifdichiarazioni. Sposterebbe l'onere della scelta di una classe specifica per il chiamante, che è esattamente ciò che il modello dovrebbe evitare. Non tutti i principi sono assoluti (in realtà, nessun principio è assoluto) e se si utilizza questo modello, si presuppone che il beneficio da esso sia maggiore del vantaggio di non utilizzare un if.


2
È perfettamente possibile creare un modello di fabbrica senza molte ifs. Vedi la risposta di @ BЈовић per un semplice esempio di come raggiungere questo obiettivo. Downvoted.
David Arno,


11
@DavidArno Naturalmente, ci sono diversi modi per scegliere la classe concreta. Service Locator è uno, un contenitore IoC configurabile è un altro. Questi sono solo dettagli di implementazione; non toglie nulla al messaggio principale di Killian, ovvero che la Factory allevia il chiamante dal dover decidere quale classe concreta istanziare. Non rimanere impantanato nei dettagli.
Robert Harvey,

1
Dichiarazione formidabile che non risponde in alcun modo alla domanda.
Martin Maat,

1
@ R.Schmitz Penso che tu sia errato nel tuo presupposto. Penso che molte persone abbiano perso questa domanda dall'OP: "Non dobbiamo modificare ShapeFactory se vogliamo aggiungere altre classi in futuro?" CHIARAMENTE, l'OP è confuso pensando che questo modello viola OCP perché per aggiungere nuove funzionalità, è necessario modificare il codice esistente. La risposta corretta a questa domanda può essere trovata nella mia risposta. La risposta breve: lasci quel codice da solo e applichi il modello Abstract Factory per ESTENDERE (non modificare) la tua funzionalità esistente. Per questo motivo la risposta di Kilian NON affronta la domanda.
hfontanez,

5

L'esempio utilizza probabilmente un'istruzione condizionale perché è la più semplice. Un'implementazione più complessa potrebbe usare una mappa o una configurazione o (se vuoi essere davvero fantasioso) un qualche tipo di registro in cui le classi possono registrarsi. Tuttavia non c'è nulla di sbagliato nell'usare un condizionale se il numero di classi è piccolo e cambia di rado.

L'estensione del condizionale per aggiungere il supporto per una nuova sottoclasse in futuro sarebbe in effetti una violazione rigorosa del principio aperto / chiuso. La soluzione "corretta" sarebbe quella di creare un nuovo stabilimento con la stessa interfaccia. Detto questo, l'adesione al principio O / C dovrebbe sempre essere valutata rispetto ad altri principi di progettazione come KISS e YAGNI.

Detto questo, il codice visualizzato è chiaramente un codice di esempio progettato per mostrare il concetto di fabbrica e nient'altro. Ad esempio, è davvero un cattivo stile restituire null come nell'esempio, ma una gestione degli errori più elaborata oscurerebbe semplicemente il punto. Il codice di esempio non è il codice di qualità della produzione, non ci si dovrebbe aspettare che sia.


Puoi spiegare come funzionerebbe una mappa / configurazione / registro?
Armon Safai,


Le fabbriche autoregistranti sono, AFAIK, impossibili in C ++ all'interno delle librerie statiche poiché le variabili globali inutilizzate (ovvero usate in modo dispari) vengono scartate dalle toolchain.
void.pointer

@ArmonSafai leggi questo per maggiori informazioni goo.gl/RYuNSM
AZ_

2

Il modello stesso non viola il principio aperto / chiuso (OCP). Tuttavia, violiamo l'OCP quando utilizziamo il modello in modo errato.

La semplice risposta a questa domanda è la seguente:

  1. Crea la tua funzionalità di base usando Pattern Metodo di fabbrica .
  2. ESTENDI la tua funzionalità utilizzando il modello astratto di fabbrica

Nell'esempio fornito, la funzionalità di base supporta tre forme: Cerchio, Rettangolo e Quadrato. Supponiamo che tu debba supportare Triangle, Pentagon ed Hexagon in futuro. Per fare questo SENZA violare OCP, è necessario creare un factory aggiuntivo per supportare le nuove forme (chiamiamolo così AdvancedShapeFactory) e quindi utilizzare AbstractFactory per decidere quale factory è necessario creare per creare le forme necessarie.


Preferisco di gran lunga la soluzione delle fabbriche autoregistranti (in assenza di un vero contenitore IoC configurabile che è il migliore), poiché altrimenti ciò che otteniamo dalla tua proposta sono fondamentalmente fabbriche di fabbriche , ed è allora che le cose diventano troppo complesse.
Nom1fan

1

Se stai parlando del modello Abstract Factory, il processo decisionale spesso non è nella Factory stessa ma nel codice dell'applicazione. È quel codice che sceglie quale factory concreta istanziare e passare al codice client che utilizzerà gli oggetti prodotti dalla Factory. Vedi la fine dell'esempio Java qui: https://en.wikipedia.org/wiki/Abstract_factory_pattern

Il processo decisionale non implica necessariamente ifdichiarazioni. Potrebbe leggere il tipo Factory concreto da un file di configurazione, derivarlo da una struttura della mappa, ecc.



Se io, il chiamante, sto prendendo la decisione su quale classe concreta istanziare, allora perché mi sto preoccupando di una Fabbrica astratta?
Robert Harvey,

Si prega di definire "il chiamante". Come descrivo nella mia risposta, c'è il codice dell'applicazione globale e poi c'è il codice che deve generare gli oggetti usando una Factory. Mentre quest'ultimo ha davvero bisogno di essere ignaro della classe concreta per istanziare, qualche altro codice contestuale deve conoscerlo e
rinnovarlo

0

Se pensi a Open-Close a livello di classe con questa fabbrica stai creando un'altra classe nel tuo sistema Open-Close, ad esempio se hai un'altra classe che prende una Shape e calcola l'area (esempio tipico) questa classe è OpenClose perché può calcolare l'area per nuovi tipi di forme senza modifiche. Quindi hai un'altra classe che disegna la forma, un'altra classe che prende N forme e restituisce quella più grande e puoi pensare in generale che le altre classi nel tuo sistema che si occupano di forme siano Open-Close (almeno sulle forme). Dal punto di vista del design, la fabbrica consente al resto del sistema di essere Open-Close e, naturalmente, di fabbrica, NON È Open-Close.

Ovviamente puoi anche aprire e chiudere questa fabbrica, tramite una sorta di caricamento dinamico e l'intero sistema può essere Open-Close (ad esempio puoi aggiungere nuove forme facendo cadere un vaso nel percorso di classe). È necessario valutare se questa ulteriore complessità vale a seconda del sistema che si sta costruendo, non tutti i sistemi richiedono funzionalità collegabili e non tutto il sistema deve essere completamente Open-Close.


0

Il principio aperto-chiuso, come il principio di sostituzione di Liskov, si applica agli alberi di classe, alle gerarchie ereditarie. Nel tuo esempio, la classe factory non si trova nell'albero genealogico delle classi che istanzia, quindi non può violare queste regole. Vi sarebbe una violazione se GetShape (o più appropriatamente, CreateShape) fosse implementato nella classe base Shape.


-2

Tutto dipende da come lo implementi. È possibile utilizzare std::mapper contenere i puntatori a funzioni per la creazione di oggetti. Quindi il principio di apertura / chiusura non viene violato. O interruttore / caso.

Ad ogni modo, se non ti piace il modello di fabbrica, puoi sempre usare l'iniezione delle dipendenze.


6
In che modo switch / case è migliore dei condizionali? L'uso di una mappa / dict / tabella per rappresentare il codice come dati è buono, se in realtà è necessario un registro di diverse implementazioni, ad esempio in alcune implementazioni di container DI. Ma avere callback diversi dello stesso tipo non è necessario per la maggior parte delle fabbriche! Non capisco bene perché lo stai suggerendo. Inoltre, molti contenitori DI sono implementati in termini di oggetti di fabbrica, quindi suggerire di usare DI invece di fabbriche sembra un po 'circolare.
am

1
@amon Volevo usare altri tipi di DI - non in fabbrica.
BЈовић


1
In che modo la tua fabbrica deciderà quale puntatore utilizzare? Alla fine devi prendere una decisione.
whatsisname

@ArmonSafai ???
BЈовић
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.