Is er een manier om een class property af te dwingen?
Stel we hebben een abstracte class Bar. Hierin zou ik graag een abstracte class property in willen opnemen die door de child classes moet worden ingevuld. Dit lijkt echter niet te kunnen:
Nee, dat kan niet. Gebruik ipv een getter om af te dwingen. Zie hier weer zo'n zelfde geval als in het interface topic: je moet een gedrag afdwingen (method), niet in implementatie (property).
@Erwin: het zou fijn zijn als wanneer iemand een vraag stelt je daar niet meteen een waarde-oordeel aan hecht. Niet iedereen heeft dezelfde kennis als jij. Ik stel een serieuze vraag en vind het niet echt prettig als jij dat dan afdoet als "een beetje kolder". De ene mens weet veel van onderwerp A en de andere mens weet veel van onderwerp B. Zo gaat het nu eenmaal in het leven. Probeer dat feit aub te respecteren.
Ik heb een abstracte class. Ik wil aan de child classes een makkelijk leesbare naam geven. In de abstract class wil ik dan een getName method maken die de class property $name returnt. Maar dan moet dus wel iedere child class de property $name hebben. Vandaar dat ik me afvraag hoe je dat het beste kunt afdwingen.
Dat hoeft niet hoor Erwin. Je bijdrages zijn altijd ZEER nuttig en ik leer daar ook van. Alleen de sarcastische ondertoon mag soms een beetje minder ;) Sommige dingen zijn voor jou vanzelfsprekend en voor mij (nog) niet. Een forum is een ideaal medium om zelf dingen te leren en om dingen aan iemand anders te leren. Je moet er altijd vanuit gaan dat mensen met een reden een bepaalde vraag stellen... ook al kan dat in jouw ogen nog zo dom zijn. Blijkbaar weet iemand iets niet en dan is het toch alleen maar leuk als je iemand kunt helpen? Zo kijk ik er in ieder geval tegenaan. En ja, jij bent een stuk vergevorderder in deze materie dan ik, en juist daarom is het fijn als je mij helpt... maar dan graag zonder te zeggen dat ik dom of idioot ben :D
Je kunt het afdwingen met __construct($name) voor een constructor met een vereist argument. Daarna maak je zowel de constructor als de setter final om alle childs te dwingen deze methoden te gebruiken en daarmee dus de vereiste eigenschap.
<?php
abstract class Foo
{
private $Name;
/**
* @api
*
* @access final public zodat altijd deze constructor wordt gebruikt en
* alle childs daarmee de vereiste parameter $name implementeren.
*/
final public function __construct($name)
{
$this->setName($name);
}
final public function getName()
{
return $this->Name;
}
/**
* @internal
*
* @access final private zodat deze methode alleen intern kan worden gebruikt
* en daarmee de constructor de enige ingang wordt.
*/
final private function setName($name)
{
if (!is_string($name)) {
throw new \InvalidArgumentException('Name is not a string.');
} else {
$this->Name = $name;
}
}
}
?>
Helaas is dit niet wat ik bedoel. Het voorbeeld van Dos komt in de buurt, maar wat ik dan als "extra" wil, is dat alle child classes zelf de property $name moeten invullen. Hmmm, een ander voorbeeld dan:
<?php
abstract class Car {
public function getConsumerName() {
return $this->consumer_name;
}
}
class Astra_Opel_X65_12AZ extends Car {
protected $consumer_name = 'Opel Astra';
}
class Escort_Ford_ABC_123 extends Car {
protected $consumer_name = 'Ford Escort';
}
?>
De abstract parent class gebruikt dus een property $consumer_name uit de child classes en nu wil ik dus dat iedere child class zo'n property heeft. Dit wil ik op de een of andere manier kunnen afdwingen. Het mag ook een constante zijn, maar het gaat er dus om dat de abstract class gebruik kan maken van een waarde die in iedere child class aanwezig is.
Dan doe je het bijvoorbeeld zo, opnieuw via een final en dus verplichte constructor in de abstract class:
<?php
abstract class Car
{
protected $consumer_name;
final public function __construct()
{
if (empty($this->consumer_name)) {
throw new \Exception('Consumer name not set.');
}
}
public function getConsumerName()
{
return $this->consumer_name;
}
}
class NoNameCar extends Car
{
// Oeps, property vergeten in te stellen
//protected $consumer_name = 'Hier had de naam gemoeten gemoeten';
}
// Gooit een exception
$car = new NoNameCar();
?>
Een alternatief is de exception uitstellen door deze te verplaatsen naar getConsumerName() omdat je daar de naam pas gebruikt.