CHAPTER 4 ■ ADVANCED FEATURES
I create a super class named DomainObject. In a real-world project, of course, this would contain
functionality common to its extending classes. Then I create two child classes, User and Document. I
would like my concrete classes to have static create() methods.
■Note Why would I use a static factory method when a constructor performs the work of creating an object
already? In chapter 12, I’ll describe a pattern called Identity Map. An Identity Map component generates and
manages a new object only if an object with the same distinguishing characteristics is not already under
management. If the target object already exists, it is returned. A factory method like create() would make a good
client for a component of this sort.
This code works fine, but it has an annoying amount of duplication. I don’t want to have to create
boilerplate code like this for every DomainObject child class that I create. How about I push the create()
method up to the super class?
abstract class DomainObject {
public static function create() {
return new self();
}
}
class User extends DomainObject {
}
class Document extends DomainObject {
}
Document::create();
Well, that looks neat. I now have common code in one place, and I’ve used self as a reference to the
class. But I have made an assumption about the self keyword. In fact, it does not act for classes exactly
the same way that $this does for objects. self does not refer to the calling context; it refers to the context
of resolution. So if I run the previous example I get this:
PHP Fatal error: Cannot instantiate abstract class DomainObject in ....
So self resolves to DomainObject, the place where create() is defined, and not to Document, the class
on which it was called. Until PHP 5.3 this was a serious limitation, which spawned many rather clumsy
workarounds. PHP 5.3 introduced a concept called late static bindings. The most obvious manifestation
of this feature is a new keyword: static. static is similar to self, except that it refers to the invoked
rather than the containing class. In this case it means that calling Document::create() results in a new
Document object and not a doomed attempt to instantiate a DomainObject object.
So now I can take advantage of my inheritance relationship in a static context.
abstract class DomainObject {
public static function create() {
return new static();
}