Alternatieve manier voor builder pattern.

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Mark Hogeveen

Mark Hogeveen

07/03/2017 16:53:30
Quote Anchor link
Als je naar de Form component van symfony kijkt, dan zie je dat een Form object een FormConfig object in zich heeft.
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.
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?
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
}
?>


Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
// 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?
Gewijzigd op 07/03/2017 17:02:10 door Mark Hogeveen
 
PHP hulp

PHP hulp

24/04/2024 23:56:54
 
Ward van der Put
Moderator

Ward van der Put

07/03/2017 17:52:45
Quote Anchor link
Harry hogeveen op 07/03/2017 16:53:30:
Dit lijkt me heel lelijk.

Wáárom lijkt je dat lelijk?

Als je weet waarom je iets lelijk vindt, weet je ook hoe je het mooier kunt maken…
Dat is niet per definitie zo — bij Kunst met een hoofdletter gaat die vlieger niet op, maar bij objectgeoriënteerd programmeren wel, want je kunt dan zeggen: "Dit is lelijk want je kunt het beter zus-en-zo doen om die-en-die reden."
 
Mark Hogeveen

Mark Hogeveen

07/03/2017 22:50:47
Quote Anchor link
Het lijkt me lelijk dat je dan een class moet maken die in elke method controleert of de instance locked is.
Je krijgt dan in elke method een terugkomend stukje:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
<?php
 if($this->locked) {
     throw new \Exception('Het object kan nu niet meer veranderd worden...');
 }

        
 // ... Ga verder met inhoud van functie.

?>


En daarbij, stel ik me bij object cloning iets anders voor. Bij een clone denk ik aan iets dat precies hetzelfde is, maar in werkelijkheid is dat niet zo in dit geval, want direct na de insrtance die je van de kloning krijgt, wordt het object immutable gemaakt d.m.v. de property locked die true wordt.
Dat immmutable maken, zou ik juist het liefste doen door op een of andere manier een access modifier te gebruiken, zoals private methods. Maar de toegangkelijkheid van methods/properties in code (public, private, protected) staat natuurlijk los van een eigen ingebouwde manier om object wijzigingen te blokkeren.
Gewijzigd op 07/03/2017 22:52:15 door Mark Hogeveen
 
Frank Nietbelangrijk

Frank Nietbelangrijk

08/03/2017 10:55:43
Quote Anchor link
Ik vind het draadje interessant maar ik mis een beetje waarom het FormConfig object "immutable" gemaakt wordt.

Wat is de gedachte hier achter?
 
Ward van der Put
Moderator

Ward van der Put

08/03/2017 11:15:35
Quote Anchor link
Dingen die je maar één keer wilt vastleggen, wil je het liefst declareren in constanten of static properties. Alleen ... at runtime kun je geen constanten meer declareren en een static property kun je niet setten. (Bovendien kun je een niet-static property ook niet alsnog static maken na het setten.) Je hebt dus soms een workaround nodig wanneer een property maar één keer geset mag worden.

Of een workaround nou foeilelijk is of juist mooi gevonden, lijkt me persoonlijk een kwestie van smaak.
 
Mark Hogeveen

Mark Hogeveen

08/03/2017 15:52:39
Quote Anchor link
Static properties zijn hier sowieso geen optie, want er kunnen meerdere (unmutable) FormConfig objecten tegeleijkertijd ergens voorkomen (elk form en subform heeft zo'n FormConfig). En de waarde van een static property geldt voor elke instance.


Ik heb er over nagedacht en het lijkt me niet dat er een andere manier is.
 
Ward van der Put
Moderator

Ward van der Put

08/03/2017 17:16:41
Quote Anchor link
Het kan altijd op een andere manier. ;-)

CakePHP kent bijvoorbeeld guarded fields waarmee je properties zelf kunt locken en unlocken:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
<?php
// Make user_id accessible.
$article->accessible('user_id', true);

// Make title guarded.
$article->accessible('title', false);
?>
 
Thomas van den Heuvel

Thomas van den Heuvel

08/03/2017 17:17:15
Quote Anchor link
Ik volg deze discussie niet helemaal maar speerpunt lijkt het niet kunnen wijzigen van objecten. Blijkbaar heb je besloten dat dit belangrijk is. Mijn vraag is: waarom is dit zo belangrijk? En zijn er geen andere manieren om te constateren dat een object gewijzigd is? Ik noem maar iets idioots: een hash van een geserialiseerd object?

Weet je zeker dat je dit alles niet te zeer "voor de vorm" doet en het praktische aspect een beetje uit het oog bent verloren? De insteek moet wel een beetje meewerken in de richting van je doel, nu lijkt het erop dat dit je alleen maar tegenwerkt :p.

Begrijp me niet verkeerd, ik wil niet de draak steken met wat je doet of probeert te bereiken en vind de discussie wel interessant (zij het wat abstract) maar soms kan het handig zijn om even een stapje terug te nemen en de huidige koers nog eens onder de loep te nemen.
 



Overzicht Reageren

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.