[oop] controlestructuur
Ik heb een vraagje... ik heb een class waarin ik de naam van een constante kan ophalen. Nu had ik in 1e instantie een method waarbij telkens 2 controles plaatsvinden, en daar heb ik nu een method van gemaakt met een extra overkoepelende controle. Dit laatste heb ik gedaan om, indien de method meer dan 1x wordt aangeroepen, geen dubbele controles te hoeven uitvoeren. Ik ben benieuwd welke versie jullie beter vinden en waarom. Hieronder even 2 voorbeeldjes van hoe het ongeveer werkt.
Voorbeeld 1:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class Language {
const DUTCH = 1;
static private $languages_values;
static public function getConstName($value) {
if (!isset(self::$languages_values)) { // controle 1
$ref_class = new \ReflectionClass(self::class);
self::$languages_values = array_flip($ref_class->getConstants());
}
if (!isset(self::$languages_values[$value])) { // controle 2
thow new \Exception('de value bestaat niet!');
}
return self::$languages_values[$value];
}
}
?>
class Language {
const DUTCH = 1;
static private $languages_values;
static public function getConstName($value) {
if (!isset(self::$languages_values)) { // controle 1
$ref_class = new \ReflectionClass(self::class);
self::$languages_values = array_flip($ref_class->getConstants());
}
if (!isset(self::$languages_values[$value])) { // controle 2
thow new \Exception('de value bestaat niet!');
}
return self::$languages_values[$value];
}
}
?>
In de opzet hierboven wordt telkens als je getConstName() aanroept eerst gecontroleerd of de property languages_values wel is geset, en daarna wordt gecontroleerd of de value in die array voorkomt.
Nu heb ik er dit van gemaakt:
Voorbeeld 2:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
class Language {
const DUTCH = 1;
static private $languages_values;
static public function getConstName($value) {
if (!isset(self::$languages_values[$value])) { // extra controle
if (!isset(self::$languages_values)) {
$ref_class = new \ReflectionClass(self::class);
self::$languages_values = array_flip($ref_class->getConstants());
}
if (!isset(self::$languages_values[$value])) {
thow new \Exception('de value bestaat niet!');
}
}
return self::$languages_values[$value];
}
}
?>
class Language {
const DUTCH = 1;
static private $languages_values;
static public function getConstName($value) {
if (!isset(self::$languages_values[$value])) { // extra controle
if (!isset(self::$languages_values)) {
$ref_class = new \ReflectionClass(self::class);
self::$languages_values = array_flip($ref_class->getConstants());
}
if (!isset(self::$languages_values[$value])) {
thow new \Exception('de value bestaat niet!');
}
}
return self::$languages_values[$value];
}
}
?>
Ik heb er dus een extra if-statement omheen gezet, die direct al kijkt of de value bestaat. Wat vinden jullie van deze aanpak?
Gewijzigd op 05/05/2014 17:44:23 door Ozzie PHP
http://3v4l.org/Bl4TO
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
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
<?php
namespace Ozzie;
use Exception;
use ReflectionClass;
class Language {
const DUTCH = 1;
private static $languages_values;
public static function getConstName($value) {
if (isset(self::$languages_values[$value])) {
return self::$languages_values[$value];
}
throw new Exception('de value bestaat niet!');
}
public static function init() {
static $initialized = false;
if ( ! $initialized) {
$ref_class = new ReflectionClass(self::class);
self::$languages_values = array_flip($ref_class->getConstants());
$initialized = true;
}
}
}
// ja, dit zet je in het zelfde bestand
Language::init();
// nee, dit dan weer niet niet
echo Language::getConstName(1), Language::getConstName(42);
// phphulp only:
?>
namespace Ozzie;
use Exception;
use ReflectionClass;
class Language {
const DUTCH = 1;
private static $languages_values;
public static function getConstName($value) {
if (isset(self::$languages_values[$value])) {
return self::$languages_values[$value];
}
throw new Exception('de value bestaat niet!');
}
public static function init() {
static $initialized = false;
if ( ! $initialized) {
$ref_class = new ReflectionClass(self::class);
self::$languages_values = array_flip($ref_class->getConstants());
$initialized = true;
}
}
}
// ja, dit zet je in het zelfde bestand
Language::init();
// nee, dit dan weer niet niet
echo Language::getConstName(1), Language::getConstName(42);
// phphulp only:
?>
Gewijzigd op 05/05/2014 18:31:51 door Dos Moonen
Ik snap de init functie niet helemaal. Eerst zet je $initialized op false, en vervolgens controleer je of ie niet false is?
Nee, maar een keer. En dan doe je in het bestand waar je de Language classe defineerd. Op die manier is Language::init() altijd uitgevoerd op het moment dat je Language::getConstName() wilt gebruiken.
"Eerst zet je $initialized op false, en vervolgens controleer je of ie niet false is?"
Ik zet initialized op false en kijk dan of ie niet true is.
http://www.phphulp.nl/php/forum/topic/name-id-description/94779/#680318 mocht je static $initialized niet snappen.
Gewijzigd op 05/05/2014 19:26:47 door Dos Moonen
Toevoeging op 05/05/2014 19:28:19:
>> En dan doe je in het bestand waar je de Language classe defineerd.
Wat bedoel je hiermee? Ik definieer niks, want ik gebruik een autoloader :-s
Ik heb het niet over het gebruik van \define()...
Ik heb het over
Code (php)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
<?
class foo {
// dit is een class definition
}
// zet dit in het zelfde bestand, op die manier is het altijd uitgevoegd wanneer foo beschikbaar is
foo::init();
?>
class foo {
// dit is een class definition
}
// zet dit in het zelfde bestand, op die manier is het altijd uitgevoegd wanneer foo beschikbaar is
foo::init();
?>
"Ik snap die static... maar je zet 'm nu toch iedere keer op false?"
Als dat zo was zou deze code 42 keer '1' moeten printen: http://3v4l.org/13gHJ
Gewijzigd op 05/05/2014 19:37:21 door Dos Moonen
Je gaat eerst constanten met een waarde definiëren, en daarna ga je de naam van de constante opvragen aan de hand van die waarde.
Jij bent zo van de real-time voorbeelden:
Ik heb een motor met een bepaald soort klep, ik ga eerst de motor uit elkaar halen om de naam van die zuigerklep te achterhalen.
In jouw voorbeeld met Language, moet je elke keer als je een taal wilt toevoegen je class aanpassen.
Als dat OOP is, ben ik Attila de Hun.
>> // zet dit in het zelfde bestand, op die manier is het altijd uitgevoegd wanneer foo beschikbaar is
Ah, op die fiets! Dat is inderdaad een goede truc!!! :)
* EDIT: kan dit ook bij een abstract class?
>> Als dat zo was zou deze code 42 keer '1' moeten printen
Oké... dan snap ik dus niet hoe die static werkt blijkbaar. Waarom verandert die niet van waarde? :-s
@Atilla de Hun:
>> In jouw voorbeeld met Language, moet je elke keer als je een taal wilt toevoegen je class aanpassen.
Kun je uitleggen wat je hiermee bedoelt? Hoezo moet ik m'n class aanpassen?
Gewijzigd op 05/05/2014 19:48:08 door Ozzie PHP
Nu willen we English erbij, dan gaat jouw methode niet werken, want die gooit een Exception.
Gewijzigd op 05/05/2014 19:56:05 door Ger van Steenderen
Correct. Dan zal je die moeten toevoegen. Maar dat moet je toch altijd? Of wil jij iedere language kunnen setten ofzo? Dus dat je dit kunt doen? $language->set('DUTCH', 1) Bedoel je dat? Leg anders even uit hoe het volgens jou wél zou moeten. Daar ben ik wel benieuwd naar.
Gegevens die dynamisch (kunnen) zijn sla je niet in de class zelf op, maar in een gegevensbestand.
Code (php)
1
2
3
4
5
6
2
3
4
5
6
<?php
$user = new User();
$user->setName('Ger');
$user->setGender(gender::MALE);
$user->setLanguage(language::DUTCH);
?>
$user = new User();
$user->setName('Ger');
$user->setGender(gender::MALE);
$user->setLanguage(language::DUTCH);
?>
Dat kan toch niet anders? Dan heb je toch constanten nodig?
Code (php)
1
2
3
4
2
3
4
<input type="select" name="language">
<option value="1">Nederlands</option>
<option value="0">Wartaal</option>
</select>
<option value="1">Nederlands</option>
<option value="0">Wartaal</option>
</select>
Dus jij gaat eerst je object aanspreken om die value's te presenteren, en daarna ga je nog eens een methode ontwikkelen om de bijbehorende inhoud weer te geven?
In principe ben je als je gegevens binnen een applicatie opslaat hard aan het coderen, en dat kan nooit je bedoeling zijn.
Ik vind dit:
$user->setGender(gender::FEMALE);
prettiger lezen en te begrijpen dan
$user->setGender(2);
Wat is daar dan precies verkeerd aan?
Hoe noem je de constructie die ik nu gebruik dan?
Wat is voor jouw het voordeel om van een iemands geslacht een object te maken?
En met alle respect voor alle ISO normen men is (of denkt) man of vrouw.
Maar je gebruikt constanten in het algemeen om een bijv. een nummer een bepaalde betekenis te geven, en niet om zomaar een hoop gegevens te genereren die je ergens anders vandaan weer moet gaan ophalen.,
Kijken we dan verder... dan vind ik het handiger om te zeggen setColor(color::red) of setLanguage(language::french), dan bijv. setColor(2) en setLanguage('fre') om maar wat te noemen.
Wat is daar dan verkeerd aan? Het is toch gewoon een werkwijze die je zelf prettig vindt?
Als jij het prettig vind om als elke keer dat er een NIEUWE taal aan je website toegevoegd wordt je code aan te gaan passen ...........
Ja, maar gaat het je er nu om of het wel of niet "fijn" is, of doe ik ook daadwerkelijk iets verkeerd? Als die landen bijv. uit een configuratie-bestand zouden komen, is het dan wel oké?
Nee. En dat voorbeeld heeft absoluut geen value OBJECTEN.
Je hebt geen object dat het mannelijk geslacht of de Nederlandse taal voorsteld.
Je wilt alleen objecten kunnen hebben die een valide waarde vertegenwoordigen.
Je zult de waarde dus moeten valideren aan de hand van bijvoorbeeld
- hardcoded waardes (gebruik dan aub constanten, magic numbers are evil)
- geconfigureerde waardes
- magie (dit is gelukkig onmogelijk)
Op die manier kun je dan een public function setGender(Gender $gender) hebben en weten dat $gender altijd valide is.
Constanten zijn handig om magic numbers (http://en.wikipedia.org/wiki/Magic_number_%28programming%29) te voorkomen. Wat jij nu doet is magic numbers wegwerken door ze als constanten met een beschrijvende naam te defineren. Dit is een goed iets.
Maar via reflection de naam van de constante (die per conventie alleen uit hoofdletters bestaan) op te gaan zoeken i.p.v. een helper method schrijven die op basis van bijvoorbeeld een switch een tekst versie produceert is niet echt mooi.
De tekst versie van een waarde hoort naar mijn idee niet af te hangen van de variabele/constante naam die naar de waarde verwijst.
Ik weet niet of jouw constructie een naam heeft...
Gewijzigd op 05/05/2014 21:24:15 door Dos Moonen