Disciplined Approach to Design An undisciplined ad hoc (“code now, think later or not at all”) approach can frequently be used to solve complete self contained well defined problems. However when confronted with more substantial, less well-defined problems – problems for which design issues involving complex structural relations between components must be effectively addressed – this undisciplined method flounders. We must first create the appropriate model for the problem and only then we must devise the appropriate realizable techniques to solve it. System Design is fundamentally a science of generalization and specialization. Generalization (or abstraction) is the process of ignoring details irrelevant to the problem at hand and emphasizing essential ones. To abstract is to disregard certain differentiating details and recognize certain commonalities. To specialize is to concentrate on the variation in the details. In the object-oriented paradigm, one must constantly think about a problem from three different levels of perspective. 1. The Conceptual level perspective concerns the concepts in the domain under study 2. The Specification level perspective concerns the interfaces of the software, not the implementation 3. The Implementation level perspective concerns the code itself. The Implementation perspective is the most used but in many ways the Conceptual perspective and the Specification perspective are the most important. At the Implementation level, an object is just code and data. At the Specification level, an object is a set of methods that can be invoked by other objects or by itself. The set of these methods define the objects public interface. At the Conceptual level, an object is an entity with responsibilities. Specifically objects are responsible for themselves. These responsibilities give the object its behavior. The conceptual approach helps us focus on what the objects are supposed to do, not simply on how to implement them. This also means we can design an object without worrying about all of the details involved. By focusing on what an object does, inheritance allows us to use different, specific behaviors when needed. It is easier to think in terms of responsibilities because that helps to define the object’s public interface. If an object has a responsibility, there must be some way to ask it to perform its responsibility. Design towards the Specification (interface) perspective to see the overall picture and to avoid being overwhelmed by details. In other words design the public interface first before the actual implementation.. Design each component so that it is given its specific public interface only by its existence in the context of the larger whole. When components are made without regard to the whole it is impossible for every component to be unique, according to its position in the whole. Abstract classes are defined at the Implementation level as classes which are not instantiated. This definition is accurate but too limiting. At the Conceptual level abstract classes are simply placeholders for other classes. They give us a way to assign a name to a set of related classes. This lets us treat this set as one concept. Abstract classes define the methods (responsibilities) their derived classes must implement. Abstract classes can also contain common methods that can be used by all derivations. An abstract class allows one to refer to all of the related classes/objects in a single consistent way yet each derived class will use the default behavior or replace it with its own variation (this is consistent with the idea that objects are responsible for themselves). Having responsibilities does not imply anything about what is inside the object. The information for which the object is responsible for may not even reside in the object itself (it may reside on a database server). There are many things that do not need to be exposed to other objects. This is the concept of encapsulation which is generally described as simply hiding the internal data members of the class. But encapsulation really refers to much more than data hiding. In general encapsulation means any kind of hiding. It can hide data, implementations, derived classes, other objects, or anything else that needs to be hidden. The advantage of looking at encapsulation this way is that it gives us a better way to split up a system. The encapsulating layers become the interfaces we design to. Encapsulation creates layers between objects or parts of a system so that we can change things on one side of a layer without adversely affecting the other side. This promotes loose-coupling between the sides. One important type of encapsulation is achieved when there is an abstract class that behaves polymorphically without the client of the abstract class knowing what kind of derived class actually is present. In this case encapsulation hides derived classes using an abstract class. Inheritance is usually presented as a way for reuse of existing classes by deriving new classes from these base classes. This suggests a kind of top-down model in which we start with a generalized class and then descend more specialized sub classes. This is certainly true but inheritance becomes much more powerful when we regard it as an important abstraction concept. Inheritance is better used as a method of consistently dealing with different concrete classes that are conceptually the same rather than as a means of specialization. This suggests a bottom-up model in which we start with a set of (varying) subclasses and use abstraction to describe this set by an abstract superclass which encapsulates the variance in the subclasses. The technique is to find what is varying and encapsulate it. Consider what should be variable in the system design. This approach is the opposite of focusing on the cause of redesign. Instead of considering what might force a design change, consider what we want to be able to change without redesign. The focus here is on encapsulating the concept that varies. Using composition of a reference to an abstract class hides the variations. The concept of using objects to hold variations in behavior is similar to the practice of using data members to hold variations in data. Both allow for the encapsulation (and therefore extension) of the data or behavior being contained. Consider the diagram shown below. Commonality analysis Conceptual perspective Specification perspective Variability analysis Abstract classes Concrete classes Implementation perspective Commonality analysis relates to the conceptual view of the problem domain and variability analysis relates to the implementation, that is, to specific cases. The specification perspective lies in the middle. Both commonality and variability are involved in the specification perspective. The specification describes how to communicate with a set of objects that are conceptually similar. Each of these objects represents a variation of the common concept. This specification becomes an abstract class or an interface at the implementation level. The abstract class is the central binding concept. An abstract class represents the core concept that binds together all of the derivatives of the class. This core concept is what defines the commonality. Commonality determines which abstract classes to use. The commonalities define the abstract classes needed. The variations determine the derivations of the abstract class. Variations identified within that commonality become derivations of the abstract classes. The specification determines the interface for abstract classes. The interface for these classes corresponds to the specification level. When defining an abstract class (commonality) we must ask what interface is needed to handle all of the responsibilities of this class. When defining derived classes we must ask given this particular realization (this variation), how can we implement it within the given specification. The relationship between the specification perspective and the conceptual perspective is this: It identifies the interface we need to use to handle all of the cases of this concept (that is, the commonality). The relationship between the specification perspective and the implementation perspective is this: Given this specification, how can we implement this particular case (this variation).