2013-04-10 9 views
6

Ho cercato di afferrare i concetti di OOP e mentre ottengo le idee generali alla base di molti di loro, spesso mi trovo ad avere bisogno di qualche consiglio riguardo alla loro implementazione pratica. Uno di questi casi è il metodo di fabbrica.Dove implementare un metodo di produzione?

Sto scrivendo un'applicazione PHP che sta per gestire le richieste in arrivo sia da interfaccia web e della riga di comando, così mi si avvicinò con la seguente struttura semplice classe di ereditarietà per coprire entrambi i tipi di richieste:

abstract class Request { 
} 

class HttpRequest extends Request { 
} 

class CliRequest extends Request { 
} 

Ora ho bisogno di un metodo factory che restituire una richiesta di esempio concreto, a seconda del valore restituito da php_sapi_name():

public function create() { 
    if(php_sapi_name() === 'cli') 
     return new CliRequest(); 
    else 
     return new HttpRequest(); 
} 

la mia domanda è: dove lo metto? Mi vengono in mente almeno tre possibilità:

1) Metodo statico in una classe separata:

class RequestFactory { 
    public static function create() { 
     // ... 
    } 
} 

2) metodo regolare in una classe separata (richiederebbe un'istanza della classe prima):

class RequestFactory { 
    public function create() { 
     // ... 
    } 
} 

3) metodo statico nella classe padre astratta:

abstract class Request { 
    public static function create() { 
     // ... 
    } 
} 

Quali sono i pro e i contro di ciascuna soluzione e che sarebbero considerati "corretti" e perché?

+1

Opzione 2. Altri non sono nemmeno OOP. –

+0

IMHO, '2)' è il migliore. Tuttavia, dovresti passare 'php_sapi_name' come parametro del metodo' create'. – Touki

+0

Anche se tutti i metodi funzionerebbero bene, raccomanderei l'opzione 1, non richiederebbe un'istanza precedente della classe factory tramite 'new RequestFactory()'. – SaschaM78

risposta

0

L'utilizzo di un metodo statico nella classe padre non mi sembra affatto una soluzione terribile. Dai un'occhiata alla classe Calendar in Java: esiste un metodo getInstance (in molti effettivamente) che restituisce un'istanza di Calendar in base alle impostazioni internazionali e ad altri criteri.

0

In questo caso io preferisco usare la classe astratta di base come il creatore l'istanza utilizzando il metodo statico (3 ° opzionale)

userò una classe esterna, come nella prima opzione quando ho bisogno di creare un po ' dipendenze che possono rompere l'incapsulamento come avere dipendenze diverse per differenti implementazioni. e renderà la classe meno manutenibile.

1

Tutte queste possibilità funzioneranno come previsto. In realtà non ottengo alcun "contro" in quanto soddisfa, ovvero IMHO, il tuo obiettivo di incapsulamento. Ora, diamo un'occhiata a Factory Method modello essenza:

Definire un'interfaccia per la creazione di un oggetto, ma cerchiamo le classi che implementano l'interfaccia decidere quale classe istanziare. Il metodo Factory consente a una classe di differire l'istanzazione alle sottoclassi.

Non sono sicuro se ciò che si è disposti a fare corrisponda perfettamente a questa definizione.

Invece, sembra che si voglia implementare qualcosa chiamato "Simple Factory" in cui il processo di istanziazione è incapsulato in una classe.

Ma avere questo tipo di metodo direttamente nella classe astratta che definisce l'interfaccia degli oggetti "Richiesta" non sembra una cattiva idea.

Come ha detto Nicolas, è un modello piuttosto comune nelle terre di Java, C# e Cocoa.

Per queste ragioni, la mia scelta sarebbe andato al 3 opzione

1

Così, ho un'idea, per fare quello che penso che vuoi, utilizzando method overloading.

class Request { 

    private $request; 
    private $valid = true; 
    private $type; 
    private $vars; 
    private $methods; 

    public function __construct() { 
     $this->type = php_sapi_name() === 'cli' ? 'cli' : 'http'; 
     if($this->is_cli()) $this->request = new CliRequest(); 
     else if($this->is_http()) $this->request = new HttpRequest(); 
     else { 
      $this->valid = false; 
      return; 
     } 
     $this->vars = get_class_vars($this->request); 
     $this->methods = get_class_methods($this->request); 
    } 

    public function __get($var){ 
     if(!$this->valid) return false; 
     if(!in_array($var, $this->vars)) return false; 
     return $this->request->$var; 
    } 

    public function __set($var , $val){ 
     if(!$this->valid) return false; 
     if(!in_array($var, $this->vars)) return false; 
     return $this->request->$var = $val; 
    } 

    public function __call($meth, $args){ 
     if(!$this->valid) return false; 
     if(!in_array($meth, $this->methods)) return false; 
     return call_user_func_array($this->request->$var, $args); 
    } 

    public function is_cli(){ 
     return $this->type == 'cli'; 
    } 

    public function is_http(){ 
     return $this->type == 'http'; 
    } 


} 

// Then, when calling the function... 
$request = new Request; 
$request->variable; // will get the variable from the Cli or Http request class 
$request->method("a","b","c"); // Will run the method from the Cli or Http request class 
0

Gli schemi di progettazione non sono limitati a OOP e molte implementazioni di schemi di progettazione OOP sono scritte con una certa gestione della memoria nel pensiero.

Vengo dal mondo Java e in Java è necessario utilizzare un modello di progettazione OOP rigoroso. Semplicemente perché tutto in Java è un oggetto. A volte è necessario creare un oggetto e un metodo anche se in realtà non è necessario per il modello stesso. Il metodo di progettazione di fabbrica è un esempio del genere. È molto buono progettare tramite interfaccia per le implementazioni della fabbrica, ma non è necessaria una classe e un metodo per implementare la fabbrica. La ragione per cui l'implementazione di un modello di progettazione a volte confonde è che il linguaggio di programmazione richiede talvolta un'implementazione che non è strettamente necessaria nel modello di progettazione stesso. La creazione di una classe con un metodo nel metodo factory è un esempio.

La mia soluzione non è puramente OOP, ma PHP non è così e, a lungo termine, non si tratta di OOP a mio parere, ma della migliore implementazione del metodo di progettazione modello di fabbrica.

Penso che l'eleganza di PHP sia che combina il meglio di entrambi i mondi. Offre una solida possibilità di progettazione OOP, ma non ha gettato via gli elementi positivi della programmazione procedurale.

Si può semplicemente creare il codice come questo:

function createRequest($pRequesttype){ 
    switch($pRequesttype){ 
    case "cli": 
     $tmp = new CliRequest(); 
     break; 
    case "http": 
     $tmp = new HttpRequest(); 
     break; 
    default: 
     $tmp = new DefaultRequest(); 
    } 
    return $tmp; 
} 

restituiscono sempre un'implementazione di default per gestire la richiesta. Un'istruzione switch è la scelta migliore per estendere il numero di scelte in un modo semplice per l'ingegnere del software.

Ora hai chiamato php_sapi_name nella tua funzione di creazione. Ti consiglio di metterlo in pratica per l'implementazione della funzione. È consigliabile lasciare che una funzione esegua un solo lavoro e ottenere la richiesta e gestire la richiesta sono due funzioni. Crea una funzione createRequest con un parametro come ti ho mostrato.

Per rispondere alla tua domanda: 1, 2 o 3? Uh, 4 in realtà. :-) Se 1, 2 o 3?

Sicuramente 1, perché io non voglio caricare troppo le classi per un metodo semplice, ma per semplificare questa situazione ho proposto la soluzione 4.

non vorrei utilizzare il metodo 2, perché non è efficace creare una lezione per un momento nel tempo. Lo considererei la migliore pratica, ma non la migliore attuazione pratica. Se si utilizza questa soluzione, si prega di supportare anche la classe con un'interfaccia per le fabbriche in generale. Il miglior design OOP, ma non la migliore implementazione pratica.

Non userei certamente il metodo 3, perché le classi astratte sono lì per astrarre gli oggetti dati e le interfacce al comportamento astratto. Un metodo factory è un'astrazione di un'interfaccia, mai di una classe astratta.

+0

Stavo guardando un elenco di domande più recenti basate sul tag php. Non sapevo che questa domanda fosse stata posta tanto tempo fa. L'ho visto solo dopo aver risposto alla domanda. :-) –

1

Per creare il codice veramente loosely coupled si potrebbe usare Ray.Di o Injektor e fare qualcosa di simile a quanto segue:

<?php 

use Ray\Di\Di\Inject; 
use Ray\Di\Di\Scope; 

/** 
* @Scope("Singleton") 
*/ 
abstract class Request { 
} 

class HttpRequest extends Request { 
} 

class CliRequest extends Request { 
} 

class ARequestConsumer { 
    /* @var Request */ 
    private $request; 

    public function __construct(Request $request) 
    { 
     $this->request = $request; 
    } 

    public function foo() 
    { 
     //... 
    } 
} 

class Global extends Ray\Di\AbstractModule { 

    public function configure() 
    { 
     $this->bind('Request') 
      ->toProvider('RequestProvider'); 
    } 
} 

class RequestProvider implements \Ray\Di\ProviderInterface { 
    /** 
    * @return Request 
    */ 
    public function get() 
    { 
     //.. factory method logic goes here that produces a concrete instance of Request 

    } 
} 



$injector = Injector::create([new Global]); 
$consumer = $injector->getInstance('ARequestConsumer'); 
$consumer->foo(); 
Problemi correlati