Software patterns first became popular with the publication of the book “Design Patterns: Elements of Reusable Object-Oriented Software”. In this book, the “Gang of Four” (Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides) describe 23 patterns for managing object creation, composing objects into larger structures, and coordinating control flow between objects.
The purpose of this project is to demonstrate several very simple ways to introduce design patterns into an evolving software project and show how those changes will benefit the software by increasing reusability and maintainability. During the course of this paper, I will discuss three unique design patterns: Abstract Factory, Composite, and Adapter. For each design pattern, I will discuss the existing problem with the software, the implementation of the pattern, and the reusability and maintainability benefits that arise from the re-architecture of parts of the software.
Reusability is the “likelihood a segment of source code can be used again to add new functionalities with slight or no modification.” (Reusability, Wikipedia) The benefits of reusability are reduced implementation time, increased test coverage, and localized code modifications when a change is required. Maintainability is the ability to easily modify or enhance a software project after it is built. The benefits of maintainability are reduced modification time and the increased ability to find and fix problems with the software. Because software usually changes over time, effort must be expended to constantly improve the design of the software to ensure the long-term success of the software project.
The Unified Modeling Language (UML) provides standard methods for describing the architecture of a software system. This paper will use class diagrams and sequence diagrams to describe parts of the software architecture.
I recently worked on a personal software project to create a plug-in for Microsoft Windows XP Media Center Edition which would allow users to browse the internet with their remote control. The software was called MCEBrowser (Media Center Edition Browser). Like many software projects, the project started out very simple and increased in complexity as features were added. Initially, the browser plug-in supported navigation to the userâ€™s home page or a URL. The control consisted of a single class:
As features were added, this single class became very large and unmanageable. In addition, the design would not support new features that I needed to add. As depicted in the class diagram, this class was tightly-coupled to the InternetExplorerOLEWebBrowser class (an OLE interface to the Internet Explorer application). When I ran into some limitations with Internet Explorer, it became obvious that MCEBrowser needed to support different web browsers. However, the existing design of the system prevented me from implementing other browser interfaces.
In this case, the overly simplistic design of the software was degraded because the requirements changed in a way that the design did not anticipate. Because requirements are often the most volatile elements of a software project, software developers must find ways to make the design resilient to requirement changes. This resiliency can be accomplished through refactoring and the implementation of design patterns. With relatively little effort, I was able to refactor the MCEBrowser code to improve the reusability and maintainability of the software.
This simple example demonstrates three software design patterns that are commonly used: Abstract Factory, Composite, and Adapter. The implementation of these patterns allows MCEBrowser to support three different web browsers, and will allow additional browsers to be added with relatively little cost. In addition, the modularization will allow for reusability and testability of individual components.
DESIGN PATTERN: ABSTRACT FACTORY
The Abstract Factory design pattern provides a way to encapsulate a group of individual factories that have a common theme. In normal usage, the client software would create a concrete implementation of the abstract factory and then use the generic interfaces to create the concrete objects that are part of the theme. The caller of the factory does not know (or care) which concrete implementation of the factory it is working with. The concrete implementations of the objects returned from the factory are also abstracted from the caller. (Abstract factory pattern, Wikipedia)
Consider the following example:
The initial implementation of MCEBrowser looked something like the following class diagram. The MCEBrowserControl class was dependant on the InternetExplorerBrowser concrete class.
An initial approach to support multiple browsers might look something like this:
In this example, an abstract Browser class has been created. There are now three concrete implementations of the Browser abstract class, OperaBrowser, MozillaBrowser, and InternetExplorerBrowser. This allows the software to support multiple browsers implementations, however the software is still very inflexible. For example, what will happen if we need to support another browser in the future? What will happen if there are other class implantations we need to create when we are using Internet Explorer? We can take the architecture one step further and solve these problems with the Abstract Factory design pattern:
In this example, the BrowserFactory is an abstract class that defines the methods that the individual factories (MozillaBrowserFactory, OperaBrowserFactory, InternetExplorerBrowserFactory) must implement. Normally, an abstract factory contains multiple creational methods to create product instances; however, in this case only one method is required. The MCEBrowserControl must initially create a concrete implementation of the abstract factory, but can use the abstract BrowserFactory class in all future references. When calling creation methods on the abstract class, such as createBrowser(), the caller does not know (or care) which concrete implementation of the Browser class is returned, as long as it is a descendant of the Browser generalization.
The flexibility of the Abstract Factory design pattern allows for great improvements in software maintainability and reusability. The specific concrete factory implementation that is created might be determined by a configuration file setting, registry setting, or information collected about the specific machine that the software is running on. If an additional browser needs to be supported in the future, we can simply add another implementation of the BrowserFactory class, along with a specific implementation of the Browser class. The MCEBrowserControl class will remain unchanged by this event. In addition, if another browser-specific class is required in the future by MCEBrowserControl, we can simply add a creational method to the BrowserFactory class and implement it in each descendant. Thus, additional browser-specific classes can be added with very little additional cost, and very little impact to the existing code. The factory and the related browser classes are now highly reusable because the creation and implementation of these classes is separated from the MCEBrowserControl class.
DESIGN PATTERN: COMPOSITE
In software architecture, a Composite is an object designed as a composition of one-or-more similar objects, all exhibiting similar functionality. This is also known as a “has-a” relationship between objects. The key concept is that you can manipulate a single instance of the object just as you would a group of them. (Composite pattern, Wikipedia)
Consider the following example:
In this example, the MCEBrowserControl is dependant on the InternetExplorerBrowser concrete class. Over time, the InternetExplorerBrowser class can become a bit unwieldy as it gains more and more responsibilities.
We can use the Composite design pattern to extract and separate the responsibilities of the InternetExplorerBrowser class into separate classes.
In this example, the moveToNextControl and moveToPreviousControl methods were extracted into the InternetExplorerBrowserHighlighter class. The clickActiveElement, setCurrentFieldValue, and getCurrentFieldValue methods were extracted to the InternetExplorerBrowserFormInputHelper class. These classes are “part of” the InternetExplorerBrowser owner class. This means that they have the same lifetime, and are owned by the owner class. When the owner class is instantiated, it is responsible for instantiating the helper classes (Highligher and FormInputHelper). Upon destruction, the owner class is also responsible for the cleanup and destruction of the helper classes. This separation of concerns allows the InternetExplorerBrowser class to stay simple and concise.
The implementation of the Composite design pattern allows for great improvements in software maintainability and reusability. The maintenance of the software is greatly enhanced because there is a separation of concerns between these classes. If there is an issue in the highlighting code, for example, the developer knows that the only class that needs to be fixed is the Highlighter class. Although only the InternetExplorer classes are shown, this software application supports multiple browsers. Because the Highlighter code is now separated from the Browser class, it is possible that some highlighting code might be able to be pulled up to a Highlighter abstract class. This would allow for increased reusability across the different browser implementations.
DESIGN PATTERN: ADAPTER
The Adapter design pattern (sometimes referred to as the wrapper pattern) ‘adapts’ one interface for a class into one that a client expects. An adapter allows classes to work together that normally could not because of incompatible interfaces by wrapping its own interface around that of an already existing class. (Adapter pattern, Wikipedia)
There are two types of Adapter design patterns: Object Adapter and Class Adapter. In the object adapter pattern, the adapter contains an instance of the class that it wraps. When calls are made to the adapter, it translates them and calls methods on the instance of the wrapped class. In the class adapter pattern, the class uses multiple inheritance to implement the existing interface, and the new interface that is needed.
Consider the following three classes:
Each of these classes, InternetExplorerOLEWebBrowser, MozillaOLEWebBrowser, OperaOLEWebBrowser, are OLE classes that define interfaces to these various web browsers. All of them support the functionality that MCEBrowser needs to use; however, they each define the interface for these operations in different ways. In order to navigate to the userâ€™s home page, a different method is defined by each browser: goHome(), navigateToHomePage(), and openStartPage(). In order to navigate to a given URL, a different method is defined by each browser: navigateToURL(), navigateToPage(), and openPage(). In this example, some of the methods even have different parameters. The Object Adapter design pattern can be implemented to adapt all of these interfaces to a common interface.
In this example, a class is created for each of the classes that need to have different interfaces. In the case of the MozillaOLEWebBrowser, the MozillaBrowser class is created with methods that are defined by the Browser abstract class. When the MozillaBrowser class is instantiated, is creates an instance of the MozillaOLEWebBrowser class. Similarly, upon destruction the MozillaBrowser instance destroys the MozillaOLEWebBrowser instance that it owns. When the caller calls the goHome() method on the adapter class, it simply translates the request and calls the navigateToHomePage method on the MozillaOLEWebBrowser instance that it owns.
As indicated by this sequence diagram, the caller (MCEBrowserControl) is not exposed to the actual interface of the MozillaOLEWebBrowser class.
The implementation of the Adapter design pattern allows for great improvements in software maintainability and reusability. The maintenance of the software is greatly enhanced because the interfaces of the various browser implementations are encapsulated in the adapter classes. If a future version of the browser class changes the interface, the adapter class is the only thing that needs to be changed. Reusability is improved because all three browser implementations can have the same generalization class. This means that some code can probably be pulled up into the generalization Browser class, and polymorphism can be used to interact with these different browser implementations.
Designing object-oriented software is difficult, and designing reusable and maintainable object-oriented software is even more difficult. Design patterns offer one way to share information about the architecture of parts of software. Design patterns allow software developers and architects to share information about good design techniques. This paper has discussed several design patterns that worked to improve the MCEBrowser software project. By learning and implementing some of these “best-practice” techniques, we can improve the design of software projects to make them more maintainable and reusable.
“Abstract factory pattern”. Wikipedia. Retrieved April 26, 2006 from http://en.wikipedia.org/wiki/Abstract_factory
“Adapter pattern”. Wikipedia. Retrieved April 26, 2006 from http://en.wikipedia.org/wiki/Adapter_pattern
“Composite pattern”. Wikipedia. Retrieved April 26, 2006 from http://en.wikipedia.org/wiki/Composite_pattern
Fowler, Martin. Patterns of Enterprise Application Architecture. Addison Wesley, 2003. ISBN: 0-321-12742-0
Gamma, G., Helm, R., Johnson, R., Vlissides, J. Design Patterns: Elements of Reusable Object-Oriented Software. Addison Wesley, 1995. ISBN: 0-201-63361-2
Kerievsky, Joshua. Refactoring to Patterns. Addison Wesley, 2005. ISBN: 0-321-21335-1
Martin, Robert C. Agile Software Development: Principles, Patterns, and Practices. Pearson Education, Inc., 2003. ISBN: 0-13-597444-5
“Reusability”. Wikipedia. Retrieved April 26, 2006 from http://en.wikipedia.org/wiki/Reusability