Dat FormConfig object bevat eigenlijk bijna alle eigenschappen van het formulier.
Het FormConfig object bevat heel veel methods, die allemaal bedoeld zijn om het object de juiste eigenschappen te geven, zoals eventlisteners, datamappers, etc. (Dus bijvoorbeeld addEventListener(), setDataMapper() )
Een formulier wordt gemaakt met een FormBuilder.
Als je dus een formulier wil maken met een builder, dan zou de builder op zijn minst ook alle methods moeten bevatten die FormConfig heeft. Dat is zo omdat je via het FormBuilder object communiceert (addEventListener(), setDataMapper()) om dit weer in de FormConfig te krijgen.
De builder zou dan een doorgeefluik zijn.
Om dit op te lossen, is in het framework de FormBuilder eigenlijk een extender van een FormConfig.
Daardoor krijgt de FormBuilder automatisch de methods die FormConfig heeft.
Uiteindelijk wordt de builder gecloond.
Dit lijkt me heel lelijk, maar het is wel een goede oplossing in plaats van doorgeefluik code.
Hier is een code voorbeeld van wat ik bedoel. Het is heel erg vereenvoudigd.
<?
class Form {
private $config; // FormConfig object dat in elk formulier de eigenschappen bevat.
public function __construct(FormConfigInterface $config) {
$this->config = $config;
}
}
class FormConfig {
public $locked = false;
public function addEventListener(...) {
// In elke method zie je deze controle
if($this->locked) {
throw new \Exception('Het object kan nu niet meer veranderd worden...');
}
// ... Ga verder met inhoud van functie.
}
public function setDataMapper(...) {
// In elke method zie je deze controle
if($this->locked) {
throw new \Exception('Het object kan nu niet meer veranderd worden...');
}
// ... Ga verder met functie.
}
// Nog heel veel functies om dit object te vullen.
// De functie om definitief de immutable FormConfig te krijgen:
public function getConfig() {
$conf = clone $this; // Er wordt een kloon van dit object gemaakt.
$conf->locked = true; // Er wordt een variabele op true gezet, die het object immutable maakt.
}
}
class FormBuilder extends FormConfig {
public function add($...) {
// element (sub form) toevoegen
}
public function get($name) {
// ...
}
// andere methods die specifiek aan de FormBuilder zijn
}
?>
// Gebruik van de FormBuilder als je een formulier wil maken
$builder = // FormBuilder instance
$builder->add(...); // Formulier element (in Symfony eigenlijk een sub form).
$builder->addEventListener(...); // Als je een listener wilt toevoegen. Deze method wordt door de builder overge-erft van FormConfig.
// Wanneer het formulier definitief is, wordt ergens in het framework de definitieve, immutable FormConfig gekregen, door FormConfig->getConfig();
Wat je hier dus ziet is dat een object wordt gecloond omdat de property variabelen er toe doen, maar de methods niet. Want de methods zijn allemaal functies die het object veranderbaar maken. En dit wil je nu niet meer. Om dus tegen te gaan dat op het definitieve onveranderbare object nog methods worden gebruikt die het object kunnen veranderen, wordt in elke method gecontroleerd of de property locked op true staat. Is dat zo, dan is het huidige object dus tot stand gekomen door een kloon, en mag dus niet veranderd worden.
Dit lijkt me heel lelijk.
- Is er een andere manier om een object immutable te maken, zonder dat je in elke method hoeft te controleren of de wijziging gedaan mag worden? Dit lijkt meer op een handmatige manier van encapsulation.
Normaal gesproken, als je een object instance eigenschappen één keer wil geven, en die eigenschappen mogen daarna niet meer van buiten de class veranderd worden, dan gebruik je een constructur.
Maar voor een object zoals FormConfig, dat misschien 20 eigenschappen kan hebben is het niet te doen om een constructor te gebruiken.
Is dat misschien een reden voor de clone-en-lock manier?