template pattern vs strategy pattern
Gisteren wees Wouter mij hier op het Strategy pattern. Ik gebruik dit zelf nooit, dus ik ben er wat over gaan lezen. Zelf blijk ik vaak het template pattern te gebruiken. Nu vraag ik me af wanneer je wat gebruikt. Ik vind ze heel erg op elkaar lijken namelijk. Ik heb op Google gezocht, en blijkbaar hebben veel mensen dezelfde vraag gesteld, maar er komen zeer veel verschillende antwoorden op.
Ik heb zelf een abstracte autoloader class waarvan de loadClass method abstract is. De child class extend de abstracte class en moet deze loadClass method dan zelf invullen. Is dat correct? Of moet je hier ook een strategy pattern gebruiken?
Gewijzigd op 24/02/2014 08:22:43 door Ozzie PHP
>> Ik heb zelf een abstracte autoloader class waarvan de loadClass method abstract is. De child class extend de abstracte class en moet deze loadClass method dan zelf invullen.
Laten we aannemen dat je hier goede redenen hebt om een abstracte klasse te gebruiken in plaats van een interface... Bepaalde delen van het autoloader-algoritme staan dan al in de abstracte klasse en je gebruikt afgeleide klassen om andere delen van het algoritme in te laten vullen. Dat klinkt als een template method pattern.
Als je namespaces en de autoloader-functionaliteit van PHP 5.3+ gebruikt, is er geen reden om "at runtime" met een strategy pattern compleet andere autoloading-algoritmen te laden.
Gewijzigd op 24/02/2014 09:23:51 door Ward van der Put
Ik vind het lastig om te bepalen wanneer ik wat moet gebruiken. Stel je hebt verschillende typen voertuigen, dan zou ik zelf gelijk denken aan een abstracte Vehicle class, en een child "car" en "bike" class. Echter, Wouter wees me er gisteren op dat het dan beter is om het Strategy pattern te gebruiken.
Wouter zei ook dat het beter is om 1 User class te hebben, waarin je verschillende rollen aan een User toekent, terwijl ik toch ook vaak heb gezien dat er gebruik wordt gemaakt van een "default" user class, die dan ge-extend wordt door child classes.
Zelf zou ik bijv. eerder denken aan een "class Admin extends User", maar Wouter zegt dat dit niet klopt, omdat we allemaal Users zijn, en dat er dus geen verschillende typen users zijn. Hij zou dus zoiets doen: $admin = new User(); $admin->setRole('admin'). Hoe kijk jij daar tegenaan?
Gewijzigd op 24/02/2014 09:31:00 door Ozzie PHP
Bij webapplicaties is de front-end vaak een geheel andere applicatie dan de back-end. Een admin bestaat niet in een front-end, alleen in de back-end. Het zijn twee compleet verschillende typen users voor twee gescheiden applicaties. Ik zou dus niet eens zeggen dat een admin een user met een speciale rol is; het is geheel andere user.
Je moet ergens beginnen. En je kunt het niet té abstract modelleren, anders krijg je zo'n taxonomie:
Code (php)
1
2
3
4
5
6
7
2
3
4
5
6
7
class Animal
class Chordate extends Animal
class Tetrapode extends Chordate
class Mammaliaform extends Tetrapode
class Mammal extends Mammaliaform
class Human extends Mammal
class User extends Human
class Chordate extends Animal
class Tetrapode extends Chordate
class Mammaliaform extends Tetrapode
class Mammal extends Mammaliaform
class Human extends Mammal
class User extends Human
Gewijzigd op 24/02/2014 09:59:43 door Ward van der Put
>> Ik zou dus niet eens zeggen dat een admin een user met een speciale rol is; het is geheel andere user.
Maar valt een admin in jouw optiek dan wel onder de categorie users? Ik zie zelf een user als een gebruiker van het systeem. Een admin zie ik dan als een ander type user, namelijk een user die toegang heeft tot de back-end en daar bepaalde dingen mag doen. Maar ik zie het wel als een user. Dus ik zou dan dit doen:
class Admin extends User
Is dat hetzelfde als wat jij bedoelt?
>> Je moet ergens beginnen. En je kunt het niet té abstract modelleren, anders krijg je zo'n taxonomie:
Hehe :)
Wil je voor elke mogelijke combinatie een nieuwe user-child klasse maken?
Mijn punt, ook in eerdere topics van afgelopen week, is dat je niet steeds moet proberen met één model alles te doen.
Code (php)
1
2
3
2
3
- user (geregistreerd bezoeker/lid van de website, zoals ikzelf)
- admin (iemand die (bepaalde) zaken mag beheren en toegang heeft tot de back-end, zoals jij)
- super-admin (iemand die alles mag, zoals Bas)
- admin (iemand die (bepaalde) zaken mag beheren en toegang heeft tot de back-end, zoals jij)
- super-admin (iemand die alles mag, zoals Bas)
Voor de admins kun je dan voor bepaalde onderdelen rechten toekennen.
Wat vind je van deze gedachte?
- gebruikers: wie?
- rechten: wat?
- groepen: wie mag wat?
In een datamodel is 'groepen' dan een koppeltabel.
Nu ga je 2 dingen combineren, daar ben ik geen fan van. Want hoe ga je nu kijken of iemand iets mag? Eerst instance checken en daarna nog roles? Waarom die niet meteen overal met roles of instances werken?
Kun je een klein voorbeeldje geven van welke classes je dan zou krijgen? Kun je een concrete invulling aan jouw voorbeeld geven zodat ik beter begrijp wat je bedoelt?
>> Eerst instance checken en daarna nog roles?
Dat is wel waar het op neer komt. Stel, jij mag bijv. een bericht van iemand anders editen. Maar een "normale" gebruiker mag dat niet. Op het moment dat een normale gebruiker is ingelogd, wil ik dus niet eens checken of hij een beheer-functie mag uitvoeren, want dat mag hij sowieso niet omdat hij een gebruiker is en geen admin.
Even een stomme analogie... maar bij iemand die geen rijbewijs heeft, ga je ook niet controleren of hij met een aanhanger mag rijden. Hij heeft geen rijbewijs, dus hij mag sowieso niet rijden.
De analogie van het rijbewijs gaat niet helemaal op, want je mag pas instappen in de auto-applicatie als je autorijden-rechten hebt die zijn toegekend aan de rijbewijsbezitters-groep.
>> De analogie van het rijbewijs gaat niet helemaal op, want je mag pas instappen in de auto-applicatie als je autorijden-rechten hebt die zijn toegekend aan de rijbewijsbezitters-groep.
Oké, laten we dan zeggen dat de politie aan een jongen van 10 gaat vragen of hij mag rijden met een caravan. Anders gesteld, je gaat aan iemand (een user) iets vragen, waarvoor hij in basis geen bevoegdheid heeft. Ik heb geen vliegbewijs. Het zou dan heel raar zijn als iemand aan mij gaat vragen of ik een vluchtschema in elkaar wil zetten.
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$securityContext = new SecurityContext(); // de registry voor de security, houdt de current user vast
$authenticator = new RequestAuthentication($request, $securityContext);
$authorizator = new AccessDecissionManager($securityContext);
$authorizator->addVoter(new FormPostVoter()); // voters bepalen of een role iets mag of niet
$user = new AnonymousUser();
$authenticator->authenticate($user);
// $user in context wordt nu als het goed is een AuthenticatedUser, of anders
// nog steeds een AnonymousUser (als hij niet is ingelogd)
// ergens in je code
if ($authorizator->isGranted('EDIT_OWN', $formPost) {
$formPost->setContent($newContent);
$formPost->save();
}
?>
$securityContext = new SecurityContext(); // de registry voor de security, houdt de current user vast
$authenticator = new RequestAuthentication($request, $securityContext);
$authorizator = new AccessDecissionManager($securityContext);
$authorizator->addVoter(new FormPostVoter()); // voters bepalen of een role iets mag of niet
$user = new AnonymousUser();
$authenticator->authenticate($user);
// $user in context wordt nu als het goed is een AuthenticatedUser, of anders
// nog steeds een AnonymousUser (als hij niet is ingelogd)
// ergens in je code
if ($authorizator->isGranted('EDIT_OWN', $formPost) {
$formPost->setContent($newContent);
$formPost->save();
}
?>
Dat wil niet zeggen dat ik begrijp wat hij bedoelt ;)
Jouw voorbeeld kan ik ook niet echt volgen. Aardig ingewikkeld.
- Applicatie "auto" vereist het recht "autorijden".
- Het recht "autorijden" is toegekend aan de groep "rijbewijs B".
Op het moment dat de jongen van 10 instapt in de auto-applicatie, controleer je niet of hij mag autorijden. Je controleert alleen of hij tot een groep behoort die het voor die applicatie vereiste recht "autorijden" heeft. Al in een eerder stadium, bij het inloggen, had de user daarvoor in de groep "rijbewijs B" moeten worden geplaatst — wat niet is gebeurd bij deze jongen van 10, want die is geen lid van deze groep.
Maar je kunt het dus inderdaad op tig manieren oplossen. Bijvoorbeeld door lidmaatschap van de groep "admin" via de configuratie verplicht te stellen voor alle applicaties in de HTTP-route /admin/.
En iemand met de groep "admin" wordt dus in de back-end toegelaten, en binnen de back-end wordt dan weer gekeken of hij de juiste rol heeft. Klopt het nog wat ik tot nu toe zeg?
En stel nu dat je een groep "admin" hebt en een groep "super-admin" (waarbij deze laatste alles mag) en je wilt ergens controleren of een user een bericht mag aanpassen. Krijg je dan zoiets?
Code (php)
1
2
3
4
5
2
3
4
5
<?php
if ($user->getGroup() == 'super_admin' || ($user->getGroup() == 'admin' && $user->hasRight('edit'))) {
// bericht aanpassen
}
?>
if ($user->getGroup() == 'super_admin' || ($user->getGroup() == 'admin' && $user->hasRight('edit'))) {
// bericht aanpassen
}
?>
Of kan zo'n controle ook efficiënter?
Gewijzigd op 24/02/2014 11:37:59 door Ozzie PHP
Hierdoor kun je je voters ook heel snel aanpassen om van de ene methode (rechten door extenden) naar de andere (rechten door roles/groups) te gaan.
Maar als ik je enigszins begrijp, wordt er op het punt waar je (in dit voorbeeld) het bericht kunt editen slechts 1 controle uitgevoerd:
En die authorizator class, die kijkt dan weer of de user tot de super-admin groep behoort, of indien dit niet het geval is, hij de juiste rechten, in dit geval 'EDIT_OWN', heeft. Zoiets bedoel je?
Niet helemaal. Wat de authorizator#isGranted doet is dit:
Code (php)
Een voorbeeld van een Voter:
Code (php)
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
56
57
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
56
57
<?php
interface Voter
{
/**
* Checks if it can handle the requested token.
*
* @param string $token The requested token
* @param null|object $object The current object
*
* @return boolean
*/
public function accepts($token, $object = null);
/**
* Checks if it grants access.
*
* @param string $token The requested token
* @param User $user The current user
* @param null|object $object The current object
*
* @return boolean
*/
public function vote($token, User $user, $object = null);
}
class ForumPostVoter implements Voter
{
/**
* {@inheritDoc}
*/
public function accepts($token, $object = null)
{
return $object instanceof Post;
}
/**
* {@inheritDoc}
*/
public function vote($token, User $user, $object = null)
{
switch ($token) {
case 'DELETE':
// als de user de DELETE_POSTS role heeft mag hij forum posts verwijderen
return $user->getRoles()->has('DELETE_POSTS');
case 'EDIT':
// als de post minder dan 7 dagen oud is en de user is gelijk aan de schrijver
// óf de user heeft de EDIT_POSTS role mag de user de post bewerken
return ($object->publishTime() > strtotime('7 days ago')
&& $object->getAuthor() === $user)
|| $user->getRoles()->has('EDIT_POSTS');
case 'CREATE':
// als de user is ingelogd mag hij berichten bewerken
return $user->getRoles()->has('CREATE_POSTS');
}
}
}
?>
interface Voter
{
/**
* Checks if it can handle the requested token.
*
* @param string $token The requested token
* @param null|object $object The current object
*
* @return boolean
*/
public function accepts($token, $object = null);
/**
* Checks if it grants access.
*
* @param string $token The requested token
* @param User $user The current user
* @param null|object $object The current object
*
* @return boolean
*/
public function vote($token, User $user, $object = null);
}
class ForumPostVoter implements Voter
{
/**
* {@inheritDoc}
*/
public function accepts($token, $object = null)
{
return $object instanceof Post;
}
/**
* {@inheritDoc}
*/
public function vote($token, User $user, $object = null)
{
switch ($token) {
case 'DELETE':
// als de user de DELETE_POSTS role heeft mag hij forum posts verwijderen
return $user->getRoles()->has('DELETE_POSTS');
case 'EDIT':
// als de post minder dan 7 dagen oud is en de user is gelijk aan de schrijver
// óf de user heeft de EDIT_POSTS role mag de user de post bewerken
return ($object->publishTime() > strtotime('7 days ago')
&& $object->getAuthor() === $user)
|| $user->getRoles()->has('EDIT_POSTS');
case 'CREATE':
// als de user is ingelogd mag hij berichten bewerken
return $user->getRoles()->has('CREATE_POSTS');
}
}
}
?>
In gebruik:
Maar ik snap (enigszins) het principe... met de nadruk op enigszins :)
Waar staat voter eigenlijk voor? Het heeft toch niks met stemmen te maken?