In the definitions, we will talk about a relationship between a Source and Target objects, or equivalently between a Source and Target class. In general, there are some orthogonal properties that can be assumed by nearly any relationship:
- multiplicity: a relationship may link to one target or to N targets
- directionality: a relationship may be unidirectional (Target has no idea it exists) or bidirectional (Target has an inverse relationship towards Source).
In this kind of relationship, Source is an instance of Target and the operator instanceof, where available, returns true:
$source = new Source(); var_dump($source instanceof Target);There are two ways to obtain an is-a relationship.
Subclassing is often abused to exploit the methods inheritance. Moreover, it is a static property because you cannot change the Target superclass at runtime. Substituting it with an implementation often improves the situation because Source can then have multiple parent classes (but you must be careful that a real is-a relationship is still present).
Has-a relationships
Has-a relationships are called associations in Uml jargon, and there are two special forms of it.
There may be associations which are neither aggregations nor compositions, since they are references to an external service for example. The term composition is often used for antonomasia to indicate every kind of association, particularly in the catch phrase Favor composition over inheritance.
If done right (without an hardcoded new operator or creation method), an association is not a static relationship because you can decide at runtime what the pointer on Target or Source links to, as long as the destination object is-a implementation or subclass of the original one.
Dependencies
Another cases we may encounter is a dependency towards a Singleton. This is an awkward case because the dependency is hidden and the Api does not show it clearly. The same goes for collaborators created in the constructor or methods instead of being asked for.
It can be said that the has-a and is-a relationships are stronger cases of dependency, since an hypothetical deletion of the Target breaks Source in both cases.
Here's the catch: most of these dependencies cannot be avoided; what we can do is keeping abstractions as Target of dependencies, such as interfaces and base classes. The less code classes depends on, the more decoupled and versatile is the design: interfaces have no code at all (except for method signatures) and are the best thing to depend on in object-oriented programming. They are the ideal point of conjunction of different modules and object graphs.
Abstract and base classes have little code too, but they can grow quickly and you can inherit only from one of them, while not being able to change the Target class easily.
I hope this formal definitions of relationships will help you better comprehend the articles in the Design Pattern series, especially the ones to come.
1 comment:
Congrats on this great article series
Post a Comment