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?
Advertisement
Answer
If memory is not an objection (nowadays, it rarely is), the solution is simply to get a step further into OOP land.
The real problem for which the “child singleton” was needed was that I wanted comparisons to be reliable. Of course two different “nulled” objects, while being both “nulled” and therefore identical in this respect, are not the same object. If they have identical properties too, then the ==
operator will return TRUE, but if one of them gets slightly changed for some reason (i.e., due to a bug), ==
will return FALSE. The use of ===
PHP5 operator in singleton context will solve this problem, but introduces more complexity.
The (not so obvious to me) simple solution is to discard comparisons altogether and simply replace:
if (Slot->Surfing == Surfing::None())
with a suitable
Slot->Surfing->isNull() // or maybe isEmpty() depending on semantics
Then I can declare the isEmpty method in the parent class, and this reveals that I really needed also a more specific equals and comparison operators:
abstract Class A { ... public static function createNull() { $nulled = new A(); $nulled->setNull(); // Whatever is needed to nullify an object return $nulled; } public function setNull() { $this->nullFlag = true; // Example return $this; } public function isNull() { return (true === ($this->nullFlag)); // Or whatever else } abstract function equals($obj); abstract function compareTo($obj); } Class Surfing extends A { public function equals($obj) { if (null == $obj) { return false; } if ($this === $obj) { return true; } if (!($obj instanceOf Surfing)) { return false; } // Properties foreach(array('property1','property2',...) as $val) { if ($this->$val !== $obj->$val) { return false; } // Maybe some other check ... return true; } } ... Slot->Surfing = Surfing::createNull(); ... if (Slot->Surfing->isNull()) { ... }
An additional big advantage of this approach is that now every activity is an independent object and can be safely updated without altering other instances (if Slot->Surfing
was a nulled object, to set it to something else I needed to reassign, I couldn’t simply go ahead and modify it — this actually introduced horrid coupling and had a tremendous potential for subtle bugs).