Door
Ozzie PHP
op 05-05-2014 17:37
gewijzigd op 05-05-2014 17:44
3.829 views
Ola,
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:
<?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];
}
}
?>
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:
<?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];
}
}
?>
Ik heb er dus een extra if-statement omheen gezet, die direct al kijkt of de value bestaat. Wat vinden jullie van deze aanpak?
>> De tekst versie van een waarde hoort naar mijn idee niet af te hangen van de variabele/constante naam die naar de waarde verwijst.
Waarom is dat?
Ik zou denken, je slaat iemands geslacht op als const male = 1; en in je Nederlandse vertaalbestand zet je dan niet 1 = mannelijk, maar male = mannelijk. "Male" is dan een soort van key. Dat was mijn gedachte erachter.
In plaats van dit:
0 = onbekend
1 = mannelijk
2 = vrouwelijk
9 = niet van toepassing
krijg je dan...
female = vrouwelijk
inapplicable = niet van toepassing
male = mannelijk
unknown = onbekend
In de 2e variant is toch veel duidelijker wat je aan het doen bent?
Binnen de applicatie fungeert "male" dus als de key en de waarde die daar toevalligerwijs bijhoort is 1, maar die waarde die gebruik je in je code niet. Alleen als je het wil opslaan.
Het eerste sla je op in de database. Het tweede gebruik je tijdens het programmeren. (constante/variabele namen, de waarde die je aan een factory method geeft zodat het een value object maakt, etc)
Als je value objecten gebruikt dan wil je dat de __toString() methode de human-readable returned. En een andere methode (get<classnaam>() bijvoorbeeld) de machine-readable versie.
De human-readable data kun je van gebruiken als key om de nederlandse/engelse/franse/whatever vertaling te krijgen.
De machine-readable data sla je op in de database/json/xml/whatever.
>> Maar wat jij doet is male aan 1 hangen, terwijl 1 ook mannelijk of homme kan zijn.
Wat ik probeer (of denk) te doen is het volgende.
Binnen de applicatie gebruik ik de taal engels. Mijn "key" voor "mannelijk" wordt daarom "male". De value die bij male hoort is 1. Als ik nu een man opsla in de database, dan doe ik dus $user->setGender(gender::male). In de database wordt dan een 1 opgeslagen. Leuk voor de database, maar die 1 is voor mij niet belangrijk, want die hoef ik niet te onthouden. Ik onthou alleen maar "male". Stel nu dat een duitse gebruiker heb, dan haal ik die 1 weer uit de database. Via die getConstName maak ik van die 1 weer "male".
Wat ik dan dus vervolgens kan doen is een Frans woordenboek waarin staat male = homme en een nederlands woordenboek waarin staat male = mannelijk.
Dat is mijn intentie in ieder geval. Als ik nu in mijn gedachtengang een kapitale fout maak, dan hoor ik dat heel graag want dan moet ik het anders aanpakken. Dus graag je reactie.
@Dos:
Nu snap ik het even niet meer.
>> 0, 1, 2 en 3 zijn machine-readable
>> unknown, male, female en inapplicable human-readable
Precies! Daarom maak ik van de value weer een human-readable waarde, zodat ik deze weer kan gebruiken tijdens het programmeren. Daarmee doe ik toch precies wat jij zegt?
>> Jij komt nu wel op een begrijpelijkere waarde uit, maar dat is meer een programmer-readable versie, niet human-readable.
Ik zou uitkomen op "inapplicable", maar dat is inderdaad een programmer-readable versie. In het vertaalbestandje zou je dan krijgen:
inapplicable = niet van toepassing
Dus wat ik als "key" gebruik is een programmer-readable versie. Klopt het dan zoals ik het doe?
Nog even een andere vraag. Die language::init() in hetzelfde bestand vond ik een top-tip, maar die kun je niet in een abstracte class zetten jammer genoeg, en dan moet ik het dus in iedere child class gaan zetten. Zou op zich kunnen, maar zou handiger zijn als het in de parent class kan. Weet jij toevallig nog een andere oplossing daarvoor? Of is die er niet?
"Doe ik het nu dan goed?"
Hangt er vanaf of je probeert value objects te gebruiken of niet. Op het moment zou je miserabel falen als dat is wat je probeert.
Als je alleen magic numbers probeert weg te werken ben je goed bezig door constanten te gebruiken.
Al voelt het gebruik van reflection om de tekstuele waarde te verkrijgen nog steeds een beetje als een hack.
"Nog even een andere vraag. Die language::init() in hetzelfde bestand vond ik een top-tip, maar die kun je niet in een abstracte class zetten jammer genoeg, en dan moet ik het dus in iedere child class gaan zetten. Zou op zich kunnen, maar zou handiger zijn als het in de parent class kan. Weet jij toevallig nog een andere oplossing daarvoor? Of is die er niet?"
Je zou een speciale classloader kunnen maken die de init methode aanroept als de net geladen class een bepaalde (InitializeOnAutoload?) interface implementeert.