Field Notes: Using the Bridge Pattern

Note that the solution presented in Figures 9-12 and 9-13 integrates the Adapter pattern with the Bridge pattern. I do this because I was given the drawing programs that I must use. These drawing programs have preexisting interfaces with which I must work. I must use the Adapter to adapt them so that they can be handled in the same way.

While it is very common to see the Adapter pattern incorporated into the Bridge pattern, the Adapter pattern is not part of the Bridge pattern.

The Bridge Pattern: Key Features

Intent Decouple a set of implementations from the set of objects using them.
Problem The derivations of an abstract class must use multiple implementations without causing an explosion in the number of classes.
Solution Define an interface for all implementations to use and have the derivations of the abstract class use that.
Participants and Collaborators The Abstraction defines the interface for the objects being implemented. The Implementor defines the interface for the specific implementation classes. Classes derived from the Abstraction use classes derived from the Implementor without knowing which particular ConcreteImplementor is in use.
Consequences The decoupling of the implementations from the objects that use them increases extensibility. Client objects are not aware of implementation issues.
Implementation
  • Encapsulate the implementations in an abstract class.

  • Contain a handle to it in the base class of the abstraction being implemented.

    Note: In Java, you can use interfaces instead of an abstract class for the implementation.

GoF Reference Pages 151–162.

Figure 9-15. Standard, simplified view of the Bridge pattern.



When two or more patterns are tightly integrated (like my Bridge and Adapter), the result is called a composite design pattern.[7],[8] It is now possible to talk about patterns of patterns!

[7] Compound design patterns used to be called composite design patterns, but are now called compound design patterns to avoid confusion with the composite pattern.

[8] For more information, refer to Riehle, D., “Composite Design Patterns,” In, Proceedings of the 1997 Conference on Object-Oriented Programming Systems, Languages and Applications (OOPSLA '97), New York: ACM Press, 1997, pp. 218–228. Also refer to “Composite Design Patterns (They Aren't What You Think),” C++ Report, June 1998.

Another thing to notice is that the objects representing the abstraction (the Shapes) were given their implementation while being instantiated. This is not an inherent part of the pattern, but it is very common.

Now that you understand the Bridge pattern, it is worth reviewing the Gang of Four's Implementation section in their description of the pattern. They discuss different issues relating to how the abstraction creates and/or uses the implementation.

Sometimes when using the Bridge pattern, I will share the implementation objects across several abstraction objects.

  • In Java, this is no problem; when all the abstraction objects go away, the garbage collector will realize that the implementation objects are no longer needed and will clean them up.

  • In C++, I must somehow manage the implementation objects. There are many ways to do this; keeping a reference counter or even using the Singleton pattern are possibilities. It is nice, however, not to have to consider this effort. This illustrates another advantage of automatic garbage collection.

While the solution I developed with the Bridge pattern is far superior to the original solution, it is not perfect. One way of measuring the quality of a design is to see how well it handles variation. Handling a new implementation is very easy with a Bridge pattern in place. The programmer simply needs to define a new concrete implementation class and implement it. Nothing else changes.

However, things may not go so smoothly if I get a new concrete example of the abstraction. I may get a new kind of Shape that can be implemented with the implementations already in the design. However, I may also get a new kind of Shape that requires a new drawing function. For example, I may have to implement an ellipse. The current Drawing class does not have the proper method to do ellipses. In this case, I have to modify the implementations. However, even if this occurs, I at least have a well-defined process for making these changes (that is, modify the interface of the Drawing class or interface, and modify each Drawing derivative accordingly)—this localizes the impact of the change and lowers the risk of an unwanted side effect.

Bottom line: Patterns do not always give perfect solutions. However, because patterns represent the collective experience of many designers over the years, they are often better than the solutions you or I might come up with on our own.

In the real world, I do not always start out with multiple implementations. Sometimes, I know that new ones are possible, but they show up unexpectedly. One approach is to prepare for multiple implementations by always using abstractions. You get a very generic application.

But I do not recommend this approach. It leads to an unnecessary increase in the number of classes you have. It is important to write code in such a way that when multiple implementations do occur (which they often will), it is not difficult to modify the code to incorporate the Bridge pattern. Modifying code to improve its structure without adding function is called refactoring. As defined by Martin Fowler, “Refactoring is the process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure.”[9]

[9] Fowler, M., Refactoring: Improving the Design of Existing Code, Reading, Mass.: Addison-Wesley, 2000, p. xvi.

In designing code, I was always attending to the possibility of refactoring by following the one rule, one place mandate. The drawLine method was a good example of this. Although the place the code was actually implemented varied, moving it around was fairly easy.

Refactoring.

Refactoring is commonly used in object-oriented design. However, it is not strictly an OO thing … It is modifying code to improve its structure without adding function.


While deriving the pattern, I took the two variations present (shapes and drawing programs) and encapsulated each in their own abstract class. That is, the variations of shapes are encapsulated in the Shape class, the variations of drawing programs are encapsulated in the Drawing class.

Stepping back and looking at these two polymorphic structures, I should ask myself, “What do these abstract classes represent?” For the shapes, it is pretty evident that the class represents different kinds of shapes. The Drawing abstract class represents how I will implement the Shapes. Thus, even in the case where I described how new requirements for the Drawing class may arise (say, if I need to implement ellipses) there is a clear relationship between the classes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.112.82