Inheritance
If you're already in any way familiar with object-oriented
programming, you'll know that I have been saving the best for last. The
relationship between classes and the dynamic objects they generate
allows for much flexibility in a system. Individual Dictionary
objects encapsulate distinct sets of translation data, for example, yet
the model for these varying entities is defined in the single Dictionary
class.
Sometimes, though, you need to inscribe variation down at the class level. Remember the DictionaryIO
class? To recap, it takes data from a Dictionary
object, writes it to the file system, takes data from a file, and merges it back into a Dictionary
object. Listing 12 shows a quick implementation that uses serialization to save and load Dictionary
data.
Listing 12. A quick implementation using serialization
|
This example introduces a couple of simple Dictionary
methods -- in particular, asArray()
, which returns a copy of the $translations
array. The DictionaryIO
implementation has the virtue of simplicity. As is usual in example
code, error checking has been omitted, but even so, this is a quick and
easy way of saving data to file.
Once you have deployed a library of this sort, you soon become committed to supporting its save format. Making a format obsolete risks the goodwill of your users who may store backups in this way. But requirements change, and you may also get complaints that the output format is not easily user-editable. Such users may wish to send export files to third parties in XML format.
You now face a problem. How do you support both formats behind the DictionaryIO
interface?
One solution would be to use a conditional statement inside the export()
and import()
methods that tests a type flag, as shown in Listing 13.
Listing 13. Using a conditional statement inside the export() and import() methods
|
This kind of structure is an example of a bad "code smell" in that it relies upon duplication. When a change in one place (adding a new type test, for example) requires a set of parallel changes in other places (bringing other type tests into line), code can quickly become error-prone and hard to read.
Inheritance offers a much more elegant solution. You can create a new class XmlDictionaryIO
that inherits the interface laid down by DictionaryIO
, but overrides some of its functionality.
You create a child class using the extends keyword. Here is a minimal implementation of the XmlDictionaryIO
class:
|
XmlDictionaryIO
is now functionally identical to DictionaryIO
. Because it inherits all public (and protected) attributes from DictionaryIO
, you can do all the same things with an XmlDictionaryIO
object that you can do with a DictionaryIO
object. This relationship extends to object type. An XmlDictionaryIO
object is obviously an instance of the XmlDictionaryIO
class, but it is also an instance of DictionaryIO
-- in the same way that a person is a human, a mammal, and an animal
all at the same time and in that order of generalization. You can test
this using the instanceof
operator, which returns true if the object is a member of the indicated class, as shown in Listing 14.
Listing 14. Using the instanceof operator to test inheritance
|
This outputs:
|
Just as instanceof
accepts that $dictio
is a DictionaryIO
object, so, too, will methods accepting these objects as arguments. This means that an XmlDictionaryIO
object can be passed to the Dictionary
class' constructor, even though DictionaryIO
is the type specified by the constructor's signature.
Listing 15 is a quick and dirty XmlDictionaryIO
implementation that uses DOM for its XML functionality.
Listing 15. XmlDictionaryIO implementation
|
The details of acquiring and generating XML can be taken for
granted. There are plenty of ways of getting this done, including the
excellent SimpleXML extension. In summary, the import()
method takes an XML document and uses it to populate a Dictionary
object. The export()
method takes the data from a Dictionary
object and writes it to an XML file. (In the real world, you would
probably use an XML-based format called XLIFF, which is suitable for
importing into third-party translation tools.)
Notice that both import()
and export()
call the utility method path()
, which doesn't exist in the XmlDictionaryIO
class. This doesn't matter because path()
is implemented in DictionaryIO
. When XmlDictionaryIO
implements a method, it is this implementation that is invoked for an XmlDictionaryIO
object when the method is called. When no implementation is present, the call falls through to the parent class.
Figure 2 shows the inheritance relationship between the DictionaryIO
and XmlDictionaryIO
classes. The closed arrow denotes inheritance and points from child to parent.
Figure 2. An inheritance relationship
View Getting started with objects with PHP V5 Discussion
Page: 1 2 3 4 5 6 Next Page: Summary