Here's an situation that has haunted one of my open-source projects for quite some time.
Imagine if you could represent every front-end input form with a single backend object? An object that contains properties and anonymous functions (for bits of logic...such as perhaps a call to a database repository to query and bring in all the values for your drop-down, etc). The class would be very basic, but would be decorated by a decorator that would add all the features you need. The decorator will take each decorated client (the class that represents your form) and provide features that make things like "input cascading", "input ID prefixing to avoid name collisions", "XSS protection", etc. a breeze! Giving your HTML input some backend server-side functionality will now be easy!
However, what concerns me is that in some cases the decorated class needs to know some status from its decorator (ack! Code Smell? Coupling?). What I did to get around that is to provide extra properties in a Factory class for providing status. See simple (but long enough to be "real world") example below:
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)
}
Decorated class (there will be lots of these, each representing its own input form)
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()
}
}
}
Factory class
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...
}
}
Investigate my use of cascadeStatus
, which is the status the Decorator sets to true
. The decorated client class needs to know about this status to perform a decision in its anonymous function. This whole solution acts as sort of a Decorator + Mediator/Factory pattern. Is this the correct approach and/or use of these patterns?