Metodo factory statico nella classe base

4

An increasingly popular definition of factory method is: a static method of a class that returns an object of that class' type. But unlike a constructor, the actual object it returns might be an instance of a subclass. enter image description here

Da: link

Non viola l'OCP?

Ho anche visto questo nel codice di produzione.

Quale sarebbe l'implementazione corretta?

Posso pensare a una classe Creator separata con metodo statico, che restituisce oggetti di sottoclasse. La mia soluzione proposta non è chiaramente una fabbrica astratta in quanto non crea famiglie di prodotti, inoltre non si adatta a nessuno dei pattern creativi di GoF. Quindi sono scettico riguardo la mia soluzione.

    
posta q126y 26.06.2016 - 14:19
fonte

2 risposte

5

Secondo Apri / Chiudi Principe (OCP) :

Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.

Esaminiamo la tua architettura statica di fabbrica in questa prospettiva:

  • Immagina di voler estendere il nostro design in orizzontale con una sottoclasse ProductThree . In che modo il makeProduct() può restituire un oggetto che potrebbe essere una sottoclasse senza conoscere il costruttore delle sottoclassi? Posso pensare a due approcci:
    • Potrebbe essere necessario modificare il metodo se è implementato come un enorme switch o concatenato if . Ciò violerebbe l'OCP.
    • Potresti pensare ad un meccanismo di auto registrazione che disaccoppia Product dalle sue sottoclassi (ad esempio usando un contenitore mappa / associativo mappare un nome / identificatore di classe con un metodo di classe maker- Esempio Java ). Ciò soddisferà i requisiti di estensibilità e rimarrà conforme a OCP.
  • Immagina di voler estendere il nostro design verticalmente, specializzando ProductOne in ProductOneA e ProductOneB . Per ottenere ciò, non è possibile fare affidamento su costruttori di sottoclasse in makeProduct() . Dovresti utilizzare un makeProductOne() . Ciò richiede che la registrazione automatica sia progettata in modo tale da consentire il collegamento a cascata attraverso la gerarchia. Anche se potrebbe essere difficile, non sembra impossibile.
  • Supponiamo di poter trovare un modo per implementare il requisito di autoregistrazione nella progettazione. In questo caso è possibile sigillare completamente il metodo di fabbrica, in conformità con l'OCP.

In conclusione, il concetto di fabbrica statica è conforme a OCP. Naturalmente, l'implementazione specifica potrebbe violare l'OCP, ma solo se non prenderebbero in considerazione i requisiti aggiuntivi che ho menzionato sopra.

Tieni presente che l'utilizzo di una classe di produzione è un approccio più pulito: applica la separazione delle preoccupazioni (Evitare cioè che la fabbrica conosca le parti interne del Prodotto). Ma per quanto riguarda l'OCP, usare un metodo statico come spiegato sopra è un'alternativa accettabile.

Non posso dire per altre lingue, ma Andrei Alexandrescu ha dimostrato l'implementazione di un metodo factory in " Design moderno C ++ - Modelli generici di programmazione e progettazione applicati ". È un OCP completo che utilizza una mappa e funzioni di registrazione / annullamento della registrazione. L'unica difficoltà che lui menziona è l'identificatore del tipo che deve essere fornito alla fabbrica. Ovviamente, un enum sarebbe un problema di OCP, ma ha esplorato diverse alternative utilizzando RTTI o un generatore di ID univoco per aggirare questo problema.

    
risposta data 26.06.2016 - 16:24
fonte
2

La ragione per cui non vedi i metodi di factory static elencati nel book GoF è perché questo pattern non usa il polimorfismo in alcun modo interessante. Il tuo diagramma suggerisce questo, ma la maggior parte delle lingue non supporta la struttura che mostra. In particolare, un metodo statico non può essere anche virtuale. Non è presente alcun oggetto istanza da inviare. Non è possibile sovrascrivere un metodo statico nel modo in cui è possibile sovrascrivere un metodo di istanza (alcuni linguaggi hanno tuttavia dei metodi di classe, dove è possibile eseguire l'invio sull'oggetto classe). Mentre il libro GoF è solo preoccupato di mostrare modelli orientati agli oggetti, i metodi statici di fabbrica sono ancora uno schema comune in molte lingue.

Che cosa la pagina che hai collegato a descrive sono due modelli distinti.

Per prima cosa abbiamo lo schema comune che anziché esporre un costruttore della nostra classe, forniamo un metodo statico. Molti linguaggi ci consentono di definire più costruttori e risolverli attraverso l'overloading dei metodi, ma i metodi statici possono avere nomi diversi che sono molto più adatti ai programmatori. La pagina collegata utilizza Color oggetti come esempio. new Color(float, float, float) potrebbe essere ovvio, ma non può essere sovraccaricato con modelli di colori diversi (RGB vs HSV). I metodi statici possono disambiguare il nome: Color::make_rgb(float, float, float) contro Color::make_hsv(float, float, float) . Si tratta di una buona progettazione dell'API, ma non ha nulla a che fare con l'orientamento agli oggetti.

L'utilizzo di metodi di produzione (statici) ci offre anche maggiore libertà nell'implementazione. Potremmo mappare i colori HSV in RGB internamente. Oppure Color potrebbe essere astratto e utilizzare diverse sottoclassi per ogni spazio colore, senza esporre questa differenza all'utente. Questo dettaglio di implementazione è interamente incapsulato:

public static Color makeRGB(float r, float g, float b) {
  return new ColorRGB(r, g, b);
}

public static Color makeHSV(float h, float s, float v) {
  return new ColorHSV(h, s, v);
}

Nella mia esperienza, tali metodi rendono il codice meno ambiguo e rendono un'API più accessibile e manutenibile. In C ++, c'è il vantaggio aggiuntivo che i metodi statici possono utilizzare la deduzione degli argomenti del modello, mentre i parametri del modello di classe devono essere forniti esplicitamente in una chiamata del costruttore.

I metodi di produzione hanno un'altra caratteristica interessante: possiamo decidere nel nostro codice quale tipo di calcestruzzo istanziare. Ho usato questo in alcuni parser:

Expression makeBinaryExpression(Expression left, String operator, Expression right) {
  if (operator == "+") return new Addition(left, right);
  if (operator == "-") return new Subtraction(left, right);
  if (operator == "*") return new Multiplication(left, right);
  if (operator == "/") return new Division(left, right);
  throw new ParseException("Expected +, -, *, /, but got " + operator);
}

Hai ragione che tali metodi statici rappresentano un problema di estensibilità. Come posso aggiungere il modello di colore CMYK alla classe Color ? Non posso, senza modificare la classe Color , o sostituirla con una classe diversa ma compatibile (cambiando le importazioni, o con i modelli C ++). Se è richiesta l'estensibilità, l'API dovrebbe essere resa virtuale, torneremo su quella in un minuto.

Tuttavia, non tutto il codice deve essere polimorfico. Soprattutto se il comportamento è "puro" e non interagisce con nessuno stato esterno e quando il comportamento non cambia, i metodi non virtuali hanno ancora un posto.

Inoltre, il principio open-closed è rilevante solo quando non è possibile modificare la fonte e rilasciare una nuova versione. Questo è il caso delle API pubblicate in cui l'editore e il consumatore si trovano in organizzazioni diverse. In tal caso, deve esserci un'API chiara e questa interfaccia deve essere attentamente progettata per consentire ai consumatori di adattarla alle loro esigenze, spesso richiedendo un'iniezione di dipendenza e esprimendo coerentemente l'API in termini di interfacce implementabili dall'utente. Ma se il codice viene utilizzato solo all'interno dello stesso progetto, l'OCP non è così rilevante (basandosi su interfacce può ancora rendere più semplice il refactoring, però).

Ora torniamo al polimorfismo. Il libro GoF elenca il modello di metodo di fabbrica e lo associa a un "costruttore virtuale". L'intero punto qui è che il metodo factory è virtuale e sovrascrivibile. Questo ovviamente significa che un metodo factory sulla classe Product è inutile, dal momento che dovremmo avere un Product esistente per crearlo. Al contrario, esiste una classe Creator diversa. Il metodo factory è quindi un esempio del Pattern Method Template che può essere sostituito da sottoclassi di Creator per fornire una sottoclasse Product specifica. Abstract Factory Pattern è un esempio di un creatore con più metodi di fabbrica correlati.

Occasionalmente, i metodi di fabbrica sono anche usati come meccanismo di iniezione delle dipendenze. La classe base definisce alcuni metodi di template per costruire le dipendenze, che possono poi essere cambiate attraverso la sottoclasse. Tuttavia, la mia esperienza dimostra che l'utilizzo di piccoli oggetti strategici per aprire la costruzione delle dipendenze è un approccio più flessibile e semplice piuttosto che richiedere l'ereditarietà dell'intera classe di destinazione.

    
risposta data 26.06.2016 - 15:57
fonte