public function foo () {
if ($this->locked) throw Exception('class is locked');
}
public function is_locked() {
return $this->locked;
}
}
?>
Toen bedacht ik vandaag... is het wel correct dat foo() rechtstreeks de locked property aanspreekt. Ligt de verantwoordelijkheid of de class gelockt is niet bij is_locked()? Stel bijv. dat in de toekomst het niet alleen van de property locked afhangt, maar ook nog van een andere variabele, dan werkt de code niet meer zoals het zou moeten. Zou het daarom niet zo moeten, vroeg ik me af?
<?php
class Foo {
private $locked;
public function foo () {
if ($this->is_locked()) throw Exception('class is locked');
}
public function is_locked() {
return $this->locked;
}
}
?>
Op zich zou je denken dat dit beter is, omdat nu daadwerkelijk is_locked bepaalt of de class op slot zit.
Maar... als je die lijn doortrekt, dan zou je dus ook niet dit krijgen...
<?php
class Data {
private $data;
public function foo() {
if (isset($this->data['foo'])) // doe iets;
}
public function has($id) {
return isset($this->data[$id]);
}
}
?>
Maar dit...
<?php
class Data {
private $data;
public function foo() {
if ($this->has('foo')) // doe iets;
}
public function has($id) {
return isset($this->data[$id]);
}
}
?>
Immers, de verantwoordelijkheid of iets bestaat hebben we bij has() neergelegd.
Trek je deze lijn nog verder door, dan zou je niet dit krijgen...
<?php
class Data {
private $data;
public function count() {
return count($this->data);
}
public function foo() {
if (count($this->data) > 3) // doe iets;
}
}
?>
Maar dit...
<?php
class Data {
private $data;
public function count() {
return count($this->data);
}
public function foo() {
if ($this->count() > 3) // doe iets;
}
}
?>
Maar sla je dan niet te ver door vraag ik me af? Wie kan er iets zinnigs over zeggen?
En zie hier precies wat ik al jaren probeer te verkondigen.
Wat je doet is jouw keuze. Globaal gesproken heb je 4 opties:
1. Je kan de verantwoordelijkheid bij de getters/setters leggen. Dat betekend dat alleen deze bij de property mogen en de rest deze methods moet gebruiken (in het kort: private properties en getters/setters voor alles).
2. Je kan de verantwoordelijkheid binnen de klasse leggen, dus alles wat in de klasse gebeurd is de verantwoordelijkheid van de klasse. Hij moet er alleen voor zorgen dat dingen buiten de klasse er niet bij kunnen (in het kort: private properties en getters/setters voor buiten).
3. Je kan de verantwoordelijkheid bij de hele klasse hierarchy leggen. Hierdoor heeft dus zowel de klasse als de subklassen vrij toegang tot de properties (in het kort: protected properties en getters/setters voor buiten).
4. Je kan de verantwoordelijkheid bij de applicatie leggen. Hierdoor heeft dus iedereen vrij toegang tot de property, als jij zo stom bent om daar iets verkeerd in op te slaan is het lekker je eigen schuld (in het kort: public properties).
Wouter, dankjewel voor je reactie. Mijn vraag refereert echter niet zozeer naar visibility (waar jouw antwoord betrekking op heeft), maar naar de verantwoordelijkheid van methods.
Mijn vraag is, op het moment dat je een bepaalde method maakt, is die method dan automatisch binnen die class altijd verantwoordelijk voor de betreffende functionaliteit, dus los van visibility.
Ik kan in een method foo het volgende zetten:
<?php
class Foo {
private $data;
public function foo() {
if (isset($this->data['foo'])) // doe iets;
}
?>
Nu voegen we een has() method aan de class toe:
<?php
class Foo {
private $data;
public function foo() {
if (isset($this->data['foo'])) // doe iets;
}
public function has($id) {
return isset($this->data[$id]);
}
?>
Oké, nu hebben we dus een aparte method in het leven geroepen waarmee we zowel van buiten als binnen de class kunnen bepalen of een bepaalde key/id bestaat.
Nu is de vraag... door de komst van die has() method... is daarmee die has() method eindverantwoordelijk geworden om te bepalen of een key/id bestaat? Waar ligt nu die verantwoordelijkheid?
Mag de foo() method zelf gaan kijken via isset() of de betreffende key/id bestaat? Of ligt deze verantwoordelijkheid, als gevolg van het bestaan van een has() method, nu volledig bij de has() method en zou de foo() method er dus als volgt moeten uitzien?
<?php
class Foo {
private $data;
public function foo() {
if ($this->has('foo')) // doe iets;
}
public function has($id) {
return isset($this->data[$id]);
}
Wat heeft dit met verantwoordelijkheid te maken, in beide gevallen doe je hetzelfde, nl controleren of een bepaalde waarde geset is. Alleen in het laatste voorbeeld via een omwegje.
Al dat afschuiven van verantwoordelijkheden begint een beetje te lijken op het Amerikaanse rechtssysteem, waarin mensen die te beroerd zijn om zelf na te denken de schuld proberen neer te leggen bij een andere entiteit (meestal de leverancier van een product; in dit geval je functie is_locked) in de hoop er een leuk schadevergoedinkje uit te halen.
>> Wat heeft dit met verantwoordelijkheid te maken, in beide gevallen doe je hetzelfde, nl controleren of een bepaalde waarde geset is. Alleen in het laatste voorbeeld via een omwegje.
Dit klopt inderdaad wat jij zegt. En vandaar ook mijn vraag. Uhm... ik vind dit lastig om uit te leggen.
Nou, stel dat we in een later stadium een keer die has functie aanpassen naar zoiets:
<?php
public function has($id) {
if (isset($this->data[$id]) && $data[$id]['legal'] === true) {
return true;
} else {
return false;
}
}
?>
En de functie foo is nog steeds dit:
<?php
public function foo() {
if (isset($this->data['foo'])) // doe iets;
}
?>
Dan correspondeert dat niet meer met de aangepaste has() method. Dan ontstaat er dus een probleem. Snap je wat ik bedoel?
In dit specifieke geval (maar iets soortgelijks zou zomaar kunnen opgaan in elke denkbare situatie) zou ik de functionaliteit splitsen in een functie has() en een functie has_legal(). Het is anders niet mogelijk om onderscheid te maken tussen het niet voorkomen van een id, en het wel voorkomen, maar niet legaal zijn.
Ozzie, nu gaan we weer op een heel hoog dummy level praten. Bedenk eerst een praktisch voorbeeld en ga dan pas nadenken. Denk na over wat er zou kunnen veranderen in je praktische voorbeeld en hoe je dat dan het beste kan aanpakken. Iets wat jij hier boven laat zien is totaal niet iets wat in de praktijk voorkomt, welke gek gaat nou een waarde controleren in een functie alleen controleert of een bepaalde variabele/parameter aanwezig is?
Toevoeging op 29/03/2014 22:28:47:
En laten we ook meteen het begrip pseudo-code de wereld uit helpen. Pseudo-code betekend niet PHP code zonder praktisch voorbeeld. Psuedo-code is een praktisch voorbeeld weergegeven in een simpele weergave, zo dat je makkelijker kan nadenken over het praktische voorbeeld, ipv met moeilijke script grammatica te werken. Dan focus je namelijk teveel op syntax en kun je niet meer goed over de applicatie opzich nadenken.
Dat legal was puur een voorbeeld om aan te geven dat de has() method met iets anders wordt uitgebreid. De invulling ervan is verder niet zo relevant. Het was slechts een voorbeeldje.
@Wouter:
>> welke gek gaat nou een waarde controleren in een functie alleen controleert of een bepaalde variabele/parameter aanwezig is?
Wat bedoel je? Het kan toch zijn dat je van buiten de class wilt weten of een waarde bestaat? Daar maak je dus een has method voor.
Het voorbeeld van is_locked hierboven is overigens dus een praktisch voorbeeld waar ik het over heb.
<?php
class Data {
private $locked;
public function add(array $data) {
if ($this->locked) throw Exception('class is locked');
// data toevoegen
}
public function is_locked() {
return $this->locked;
}
}
?>
Zouden jullie de controle of de class gelocked is doen zoals je hierboven in de add() method zit? Of zouden jullie het zo doen?
<?php
class Data {
private $locked;
public function add(array $data) {
if ($this->is_locked()) throw Exception('class is locked');
// data toevoegen
}
public function is_locked() {
return $this->locked;
}
Waarom maak je dan is_locked public?
Lijkt mij een gewoon alles door elkaar gooien.
Blijft nog steeds dat je dingen dubbelop doet:
[code]
<?php
public function add(array $data) {
if ($this->is_locked()) throw Exception('class is locked');
// data toevoegen
}
?>
[code]
Is hetzelfde als:
[code]<?php
public function add(array $data) {
if ($this->locked) throw Exception('class is locked');
// data toevoegen
}
Ik maak is_locked public zodat je van buitenaf kunt testen of de class wel of niet op slot zit:
<?php
if (!$data->is_locked()) $data->add($new_data);
?>
Ik snap wat je bedoelt met dingen dubbelop doen. Dat is ook exact de kern van mijn vraag.
Een functie kan heel makkelijk kijken of de class property locked true of false is, maar mijn vraag is of een willekeurige functie dat zomaar zou moeten doen. Mag een willekeurige functie zelf die property raadplegen, of is het netter als deze functie gebruik maakt van de is_locked functie. Ook al komt het precies op hetzelfde neer. Dat is dus precies waarom ik deze vraag stel.
Ik ga even een belachelijk voorbeeld geven nu... maar ik probeer daar iets mee duidelijk te maken waar deze vraag betrekking op heeft. En hopelijk snap je dan wat ik bedoel.
Nogmaals, het is vrij belachelijk, maar het gaat om de gedachte die erachter zit.
Stel we hebben deze functies:
- add
- delete
- is_locked
De add en delete functie kunnen alleen gebruikt worden als de class NIET is gelocked.
Bert is de bewaker van de data class. Stel je de data class voor als een grote loods.
Situatie 1)
Adriaan wil iets neerleggen in de loods. Pompiedom... daar komt ie aangewandeld. Adriaan loopt naar bewaker Bert en vraagt of de loods open is. Bert loopt naar de deur. Hij drukt de klink naar beneden, en ja hoor de loods blijkt open te zijn. Adriaan legt snel zijn spulletjes neer. Een paar minuten later komt ook Dennis aanlopen. Ook hij gaat naar Bert. Bert controleert weer of de loods open is. De loods is open en Dennis haalt iets weg uit de loods.
Situatie 2)
Adriaan wil iets neerleggen in de loods. Pompiedom... daar komt ie aangewandeld. Hij ziet Bert wel staan maar negeert hem. Adriaan voelt aan de deurkruk. De loods gaat open en Adriaan legt z'n spulletjes neer. Dennis is ook weer van de partij. Ook hij negeert Bert. Voelt of de loods open is en haalt er wat spullen uit.
Op een goede dag wordt de loods ook gebruikt om gevaarlijke chemische stoffen uit te testen. Aan bewaker Bert is gevraagd of hij voortaan niet alleen wil testen of de deur niet op slot zit, maar ook of er geen giftige gassen aanwezig zijn.
Situatie 3)
Adriaan en Dennis komen bij de loods. Hallo Bert, kunnen we erin? Nee, nu even niet zegt Bert want de loods is gevuld met gevaarlijke chemische stoffen. Oké, zeggen Adriaan en Dennis. We komen later wel even terug.
Situatie 4)
Adriaan en Dennis staan voor de loods. Ze negeren bewaker Bert. Ze doen de deurkruk naar beneden en lopen gezellig pratend de loods binnen. "Erghh... wat gebeurt er... wattisssdiitt...". Boem, ze vallen allebei dood op de grond. Ze zijn omgekomen door de chemische stoffen.
Zo... mooi verhaal hè :-)
In situatie 4 ging het dus mis. Als ze Bert (de is_locked method) hadden geraadpleegd was er niks aan de hand geweest en was alles goed gegaan. En dat is dus de kern van mijn vraag. Bij wie ligt de verantwoordelijkheid? Mag zo'n add/delete functie zelf kijken of de deur op slot zit, of ligt die verantwoordelijkheid bij bewaker Bert, ofwel de is_locked functie?