Ecco una situazione che ha ossessionato uno dei miei progetti open source per un po 'di tempo.
Immagina di poter rappresentare ogni modulo di input front-end con un singolo oggetto di backend? Un oggetto che contiene proprietà e funzioni anonime (per bit di logica ... come ad esempio una chiamata a un repository di database per interrogare e inserire tutti i valori per il menu a discesa, ecc.). La classe sarebbe molto semplice, ma sarebbe decorata da un decoratore che aggiungerebbe tutte le funzionalità di cui hai bisogno. Il decoratore prenderà ogni cliente decorato (la classe che rappresenta il tuo modulo) e fornirà caratteristiche che fanno cose come "input cascading", "prefisso dell'ID di input per evitare conflitti di nome", "protezione XSS", ecc. Fornendo il tuo input HTML alcune funzionalità back-end sul lato server saranno ora facili!
Tuttavia, ciò che mi preoccupa è che in alcuni casi la classe decorata ha bisogno di conoscere un certo status dal suo decoratore (ack! Code Smell? Coupling?). Quello che ho fatto per aggirare è fornire proprietà extra in una classe Factory per fornire lo stato. Vedere un esempio semplice (ma abbastanza lungo da essere "mondo reale"):
Decorator
Class InputDecorator{
private $client;
// Inject the client object to decorate
public function __construct(InputInterface $client){
$this->client = $client;
$this->client->input = $this->client->setupInput();
}
// Perform an Input cascade
public function cascade($inputName){
foreach($this->client->input[$inputName]['children'] as $child){
$this->client->input[$child]->cascadeStatus = true; // <-- Set status here
$this->outputAJAX($child);
}
}
// Return HTML content
public function outputHTML(){
foreach($this->client->input as $input){
$dropDownValues = $input['drop-down-values'];
// take the dropdown set of values and render <select><option...
}
}
// Return Script(JQuery) content
public function outputAJAX($inputName){
$dropDownValues = $this->client->input[$inputName]['drop-down-values'];
// take the dropdown values and render $(input-name-here).html('<option...
}
// overloading magic methods here (typical of decorator pattern)
}
Classe decorata (ci saranno molti di questi, ognuno dei quali rappresenta il proprio modulo di input)
Class InputExample implements InputInterface{
public $input = array();
public function setupInput(){
$_this = $this; (for php 5.3 reasons)
$this->input['states'] = InputFactory::make("select");
$this->input['states']->class = "some CSS class";
$this->input['states']->style = "Inline styles are bad!";
$this->input['states']->children = array("cities");
$this->input['states']->values = function() use ($_this){
// ORM->query for list of states
// return array of states
}
$this->input['cities'] = InputFactory::make("select");
$this->input['cities']->class = "";
$this->input['cities']->style = "";
$this->input['cities']->values = function() use ($_this){
if($_this->input['cities']->cascadeStatus == true) // <-- status check
// return array of cities in the selected state
else
// return array()
}
}
}
Classe di fabbrica
class InputFactory implements InputFactoryInterface{
public static function make($type)
{
if ($type == "select") {
return new InputTypes\Select($id); //<-- cascadeStatus property is defined in here
} elseif ($type == "text") {
return new InputTypes\Text($id);
} elseif ($type == "checkbox") {
return new InputTypes\CheckBox($id, "hidden");
}
// Room to grow...
}
}
Esamina il mio utilizzo di cascadeStatus
, che è lo stato impostato da Decorator su true
. La classe cliente decorata deve conoscere questo stato per eseguire una decisione nella sua funzione anonima. Questa soluzione completa agisce come una sorta di Decoratore + modello Mediatore / Fabbrica. È questo l'approccio corretto e / o l'uso di questi modelli?