Motivo di progettazione in cui oggetti decorati possono chiedere cose come "aggiornamenti di stato" dal decoratore per eseguire il suo ruolo?

1

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?

    
posta David Graham 06.02.2015 - 04:18
fonte

1 risposta

-1

Credo che quello che stai cercando di fare sia reinventare Symfony Forms e aggiungere alcuni gruppi di < a href="http://en.wikipedia.org/wiki/Object-relational_mapping"> ORM a loro;)

In effetti contengono tutti i dati del modulo e tutta la struttura HTML della forma descritta in modo molto dichiarativo con possibilità di estensione.

Ma penso che tu abbia torto quando vuoi che i moduli siano in grado di interrogare i dati di back-end. Questo non è corretto.

Il modulo non dovrebbe sapere come interrogare effettivamente i dati. Hai violato SRP qui. Il modulo è solo per la gestione dell'input dell'utente e, può essere, per convalidarlo (ma la validazione effettiva deve essere eseguita per mezzo di una classe di validazione separata)

Quello che dovresti fare è interrogare i dati usando Servizi o Archivi (a seconda della complessità dell'architettura) in Controller e quindi creare Moduli usando quei dati. Quando l'utente invia il modulo, convalida i dati dell'utente lì e poi chiama di nuovo Servizio (o Repository) per archiviare tali dati nel back-end.

    
risposta data 13.02.2015 - 08:18
fonte

Leggi altre domande sui tag