|< Free Open Study >|
10.4 Other Design Transformation Patterns
Many other design transformation patterns can be observed. The following descriptions examine several of the more useful patterns.
The Inverted Inheritance Pattern
Designers of inheritance hierarchies sometimes find themselves in a situation in which they are required to override base class methods in a derived class with a NOP method, that is, a method that does nothing.
Do not override base class methods in one or more derived classes with a NOP method.
If a derived class overrides a base class method with a NOP method, it is effectively saying that the derived class does not know that particular piece of behavior. If this is true, then the logic of the design is flawed. The designer is stating:
Logically, either all Xs do not know how to f, or Ys are not special types of Xs.
If this logic is allowed in specialization hierarchies, then anything can be made to be a special type of anything else. We can simply NOP all of the undesirable methods and add our choice of new ones. The solution to these design problems is to state that the derived class is not a special type of the base class. Both classes are derived classes of some undiscovered base class.
The inheritance hierarchy should be redefined such that both the base and derived classes inherit from some common base class.
In some cases, this pattern can be further refined via the specialization pattern. These cases include those derived classes that do not have any additional messages beyond that of the base class.
The Lexical Scope Pattern
Some designs demonstrate a uses relationship between two classes contained within the same class, that is, the two classes share lexical scope.
Classes that are contained within the same class should not have a uses relationship between them.
A uses relationship between two classes contained within the same class is redundant. The containing class already has an implied uses between itself and each of the two contained classes. Any behavior needing a combined effort between the two contained classes should be conducted by the containing class to keep the contained classes free of extra dependencies. This simplifies the design and increases the reusability of the contained classes.
All coordination between two contained classes should be carried out by the containing class.
The One-Instance Pattern
There exist inheritance hierarchies in which each derived class has only one instance in a given domain, but each derived class has its own methods for the base class messages.
Be wary of derived classes that possess only one instance. Be sure the derived classes are not objects of the base class.
Class designers often model objects, whose behaviors differ, as derived classes of some base class. Modeling objects as classes leads to class proliferation. What the designer really needs to do is generalize the objects into a new class. This is more complicated when the behavior of each object is different. The solution is to model the different behaviors as some data member (a formula) and a generic method (an interpreter of the formula). In any event, it makes no sense to have several related classes, each of which can instantiate only one object.
The derived classes need to be generalized into one class with a formula and an interpreter of that formula.
The Data-Hiding Pattern
A class designer will often argue that public data (in a class) is necessary in order to implement some feature of his or her application. This public data is used by the application directly, rather than going through the public interface of the class.
A class should support strict data hiding of its implementation.
If a class has a public data member, it is not possible to find all of its dependencies without examining all of the application code. This examination becomes a management nightmare. Designers should ask themselves why they are making a particular data member public, what they are doing with the data member, and why the class that owns the data does not perform the behavior for them. In all cases, the public data member can be replaced by a public operation. In the worst case, the public operation returns a copy of the data in question (an accessor method). In the best case, the key abstraction represented by the class takes on the necessary higher-level behavior desired by the designers.
The class designers should endeavor to make all of the data private, reserving the public section of their classes for operations on that data.
|< Free Open Study >|