What are the causes of System Redesign?
To get started on Part 2 of choosing a design pattern, we need to look at the causes of redesign. I realize that his is jumping ahead in the list, but because the issue of re-design is at the heart of design patterns, it’s a good place for the second discussion of choosing a design pattern. The Gang of Four point out eight causes, and these causes each have design patterns that will help you avoid this redesign (see pp. 23-25 in GoF.) In this session, we’ll review the eight causes and the suggested patterns that can be used to avoid redesign. Look for the cause of redesign that best fits your system or general type of app.
Creating an object by Specifying a Class Explicitly
In one sense, this cause for redesign can be handled by the what you might do in making a movie deal–
Talk to my agent…
You don’t want to deal with the lawyers, contracts and all the negotiations required to make a movie deal. You just want to read the script, rehearse and get your lines straight. You let Murray (your agent) do all of that for you so you don’t get all tangled up with details you don’t know about or want to know about. Right away, you may be thinking,
That sounds like a Factory…
That’s right! You let the factory deal with the class your client wants to make a request from. A Proxy or Prototype would be another choice for separating request from the class you’re making the request to.
Dependence on specific Operations
Another cause for re-design is a dependence on specific operations which may change. The reason for changing an operation or a set of operations can be anything from a bright idea you get for a better way to set up the operations to changes in the objects the used by the operations. It could also be due to a new operation you want to add to the system. Suppose your system has an operation that handles the request for a phone number. The number could be anything from a land-line phone to a Skype number or something in the future none of us have even though of. You don’t want change in or the addition of a single operation to cause re-design for the whole site. So avoid hard-coded requests and let the decisions be made at compile time or run time. Two examples that avoid tightly bound requests are the Chain of Responsibility and Command patterns.
Dependence on Hardware and Software platforms
If you’re programming on a fairly low level, hardware differences can be more pronounced because you can program directly to the processor. PHP is a server-side language sent to a browser and it doesn’t matter is the user’s system is Linux, Windows and Mac operating systems. So platform is not something we tend to worry about or even have to concern ourselves with—albeit a few exceptions.
Having written design patterns for PHP5+, ActionScript 3.0 and a little Java, I am able to preserve most structures in a cross-language environment. However, I do need to attend to language differences, such as the fact that PHP5+ is weakly typed and ActionScript 3.0 is strongly typed. Gamma and associates suggest the Abstract Factory and Bridge designs to help overcome any software dependencies that may arise. The high level abstraction in both patterns helps to ameliorate any differences in hardware and software. However, most design patterns encourage communication between interfaces rather than concrete implementations, and so I would suggest that most (if not all) design patterns have a healthy regard for cross-platform and cross-API cases.
Dependence on Object Representations or ImplementationsWe assume when we build a client class that it will simply make requests. However, to make those requests it has to know how an object is represented, stored, located, and implemented. Rather than having that responsibility thrust upon the client, GoF suggest hiding that information from the client. Then if changes are made, the client can make the same request and the information will be handled through abstract structures where change will not ripple through to the client nor the program. Figure 1 suggests that the Client doesn’t know what it’s getting itself into, and in this case, that’s a good thing! The Abstract Factory and Bridge designs along with Memento and Proxy designs keep request processing information hidden from the client, and so when changes are made, the client can make the same requests but get different implementations representing any changes to the system.
Dependence on Algorithms
Most programs have a set of algorithms and some may have to be changed. Where your system is set up so that changing an algorithm will ripple through your program causing errors, you need to isolate the algorithms. For example, an algorithm can change so that a set of methods require additional parameters to deal with the revised algorithm. A simple change from a circle to an ellipse algorithm can cause far-reaching problems.
Several design patterns are developed to deal with changing algorithms. The Gang of four suggest the following:
Because so many different patterns are designed to deal with changing algorithms, they are likely to represent a common reason for re-design. So, you may be well-served to first ask whether any algorithms in your system are likely to change.
Tight CouplingBy focusing of re-use, you can better see where you may have issues that can be resolved by design patterns. The difficulty in spotting tight coupling lies in the fact that you often don’t see that coupling until you want to make changes or re-use the class. If a class is connected to another class through an implementation instead of an interface, your chances of tight coupling are pretty good. If your class is set up through an abstract class or interface, it can more easily be re-used because it is not dependent on implementations. Several design patterns help to reduce such dependencies and the tight coupling that accompany such coupling:
- Abstract Factory
- Chain of Responsibility
- Observer (Chapter 14 in Learning PHP Design Patterns)
At this point, you can see where both algorithm change and tight coupling account for a huge amount of redesign. As you look at the other design patterns, see where your program has one or more reasons for redesign. Where the most intersections occur, ask yourself,
Which of these redesign causes are in my program?
Then find the “intersection” where the same design pattern is used to solve those problems.
Extending Functionality by SubclassingOne of the key principles of design patterns is favoring composition over inheritance. The more levels of subclassing, the less likely you can re-use a class. Figure 3 characterizes subclassing as a house of cards that can easily fall—cause re-design. However, the problem with using a house of cards as an analogy, is that the base of a house of cards is made up of the most triangle structures. Subclassing is like turning a house of cards on its head and building on a single class rather than a base of several classes. Delegation, on the other hand, expects some class autonomy—otherwise, how can you farm out (delegate) operations to different classes? However, that class autonomy can be rooted in an interface making it easier to delegate to many different classes and not just one. The following design patterns are good ones to find delegation in use: Bridge, Chain of Responsibility, Composite, Decorator, Observer and Strategy.
Inability to alter classes conveniently
The final cause for re-design GoF note occurs where you want to modify a class that you may not have full access to. Such cases could exist where you may not have access to the class’ source code (such as ones in libraries.) They suggest using patterns such as the Adapter, Decorator or Visitor to wrap in some code without altering the original system.
Copyright © 2013 William Sanders. All Rights Reserved.