The title is maybe not too clear, so here's an example.
abstract Class A {
public static $property = null;
}
Class B extends A {
}
Class C extends A {
}
I want to have a "None" method in all classes extending A, and this method should return an instance - always the same - of the called class. So:
B::None() // Returns a "default" B
C::None() // Returns a "default" C
Why is this: I have (simplifying) several Slots which may or may not be assigned to Activities of several kinds. So I can have a Slot for Surfing and one for Swimming. And that Slot may be null. In my code, when reporting, I could of course do something like
if (Slot.Surfing == null) {
println "Not surfing anywhere";
} else {
println Slot.Surfing.Location;
}
But I'd like to not check at all and just write
println Slot.Surfing.Location;
and pre-assign the slot to Surfing::None(). Actually I'd do that in a superclass and have it automatically assign the "proper" instance of None.
This way, Slot.Surfing(nowhere) is a different null from Slot.Swimming(nowhere), but that for me now would actually be a feature.
The problem is that if I really want to check I'm swimming somewhere, I must be sure that
if (Slot.Surfing == Surfing::None()) {
works. For that, None() must always return the same object. I could run the check on a field of Surfing, maybe a non-i18n-ed integer value... 0 or -1 being a typical choice... but adding a property for that purpose seems ill designed.
NOTE: this has many similarities with the Singleton (anti)pattern, but it is not actually a Singleton (see at bottom).
If I implement the None()
method in A, though, I have to handle the fact that any static property will "live" only once, in A, and not in each of the child classes. So I create a default instance of B, save it in A, and then all subsequent calls to other subclasses of A will find and return that one instance -- and C::None()
will be an instance of B.
I can make the None() method create a new default instance every time. This works, but now I have several "default" Bs, and in some circumstances two properties both set to B::None()
will be, quite correctly, considered different.
In the end I came up with this workaround (PHP 5.3.+):
private static $nones = array(); // array of already created "null" instances
protected static function None() {
// If I do not already have an instance for this class...
if (!isset(self::$nones[$ChildName = get_called_class()])) {
// ... I create a default instance.
self::$nones[$ChildName] = new $ChildName(/*...*/);
}
// And I return the default instance.
return self::$nones[$myClass];
}
I have checked some questions and answers on Stack Overflow and elsewhere, and the most relevant one employs what amounts to the same approach (notice the $instance
array indexed on the called class's name):
class A {
public static function getInstance(){
// Maybe use this function to implement the singleton pattern ...
return self::$instance[get_called_class()];
}
public function className(){
return get_class(self::getInstance());
}
}
Yet maybe because I'm still a wet-behind-the-ears OOPer, to me this approach smells. I would think there ought to be a way of declaring a child static property in the parent, and accessing it from the superclass (of course, then I would have to ask myself: if A declares a "downstream" static property, B inherits from A, and C from B, where does that property live, or where should it live, now? -- and I have no satisfying answer).
Addendum - Singletons
The above approach is in practice not too different from a Singleton
. It seems (thanks to Touki for pointing me there) that I can get rid of Singletons through dependency injection. Yet in this case it would require to pass around, say, None_C
to all methods that may require a default value for a reference to an instance of C. I would then have to push None_C
into my Configuration
object, and have it know about any subclasses of A that I might declare. At first sight, this smells even more (although in fact adding another subclass of A amounts to changing the system's configuration... which would be the reason for changing Configuration
).
TL;DR
So to make a long story short, granted that the above approach does work,
- is having the parent class maintain a static array of its "active" children acceptable from an OOP point of view?
- is there a better and/or cleaner way to do it?