Let's re-examine the shapes example discussed at the beginning of the chapter. How would I implement it in an object-oriented manner? Remember that it has to do the following:
1. |
Locate the list of shapes in the database. |
2. |
Open up the list of shapes. |
3. |
Sort the list according to some rules. |
4. |
Display the individual shapes on the monitor. |
To solve this in an object-oriented manner, I need to define the objects and the responsibilities they would have.
The objects I would need are:
Class | Responsibilities (Methods) |
---|---|
ShapeDataBase | |
Shape (an abstract class) | display —defines interface for Shapes |
Square (derived from Shape) | |
Circle (derived from Shape) | |
Collection | |
Display |
The main program would now look like this:
Main program creates an instance of the database object.
Main program asks the database object to find the set of shapes I am interested in and to instantiate a collection object containing all of the shapes (actually, it will instantiate circles and squares that the collection will hold).
Main program asks the collection to sort the shapes.
Main program asks the collection to display the shapes.
The collection asks each shape it contains to display itself.
Each shape displays itself (using the Display object) according to the type of shape I have.
Let's see how this helps to handle new requirements (remember, requirements always change). For example, consider the following new requirements:
Add new kinds of shapes (such as a triangle). To introduce a new kind of shape, only two steps are required:
- Create a new derivation of Shape that defines the shape.
- In the new derivation, implement a version of the display method that is appropriate for that shape.
Change the sorting algorithm. To change the method for sorting the shapes, only one step is required:
- Modify the method in Collection. Every shape will use the new algorithm.
Bottom line: The object-oriented approach has limited the impact of changing requirements.
There are several advantages to encapsulation. The fact that it hides things from the user directly implies the following:
Using things is easier because the user does not need to worry about implementation issues.
Implementations can be changed without worrying about the caller. (Since the caller didn't know how it was implemented in the first place, there shouldn't be any dependencies.)
The insides of an object are unknown to outside objects—they are used by the object to help implement the function specified by the object's interface.
Finally, consider the problem of unwanted side effects that arise when functions are changed. This kind of bug is addressed effectively with encapsulation. The internals of objects are unknown to other objects. If I use encapsulation and follow the strategy that objects are responsible for themselves, then the only way to affect an object will be to call a method on that object. The object's data and the way it implements its responsibilities are shielded from changes caused by other objects.
Encapsulation saves us.
|