CSC/ECE 517 Fall 2011/ch7 7d rt
AntiPatterns in Software Development
Introduction
The term antipattern was coined by Andrew Koenig<ref name = koenig/>, in 1995. His inspiration was a story told about Thomas Edison's many failed attempts to find a suitable material for the filament of a light bulb. When asked if he was discouraged, Edison replied that indeed he was not; he now knew hundreds of items that wouldn't work.
Koenig believed that the same philosophy should be applied to software development. As he studied the book Design Patterns presented by the Gang of Four<ref name = gof/>, he felt that it was just as important to identify potential pitfalls as well as positive practices. He named these non-solutions antipatterns. He defined an antipattern as "just like a pattern, except that instead of a solution it gives something that looks superficially like a solution but isn't one." <ref name = koenig/>
In 1998, a different group of four expanded on this idea publishing AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis<ref name = ap/>. The book identified antipatterns from three different viewpoints: the software developer, the software architect and the software manager. The authors used two criteria to distinguish antipatterns:
- It was a frequent occurrence, that initially seemed to be beneficial, but ultimately was not and
- There is a alternate, preferred solution that is proven and repeatable.
Through the years the concept of antipatterns has been further extended to apply to additional areas of software development as well as areas outside the realm of programming. This article will address software development antipatterns.
Just like patterns, antipatterns have certain elements. They include:
- Name so that they can be identified.
- A description of why the bad solution might be attractive.
- An explanation of how that solution is bad long-term.
- Suggestions for other patterns that provide better solutions.
There are several catalogs of antipatterns available as well as a number of books that address the topic. Below we will explore a few of the more common antipatterns.
AntiPatterns
Call Super
Description
We are all familiar with the concept of inheritance in object-oriented programming where a subclass takes on the properties and actions of a superclass. The subclass can then override the methods of the superclass either replacing or augmenting the functionality provided in the superclass. The call super antipattern requires subclasses to override methods of the super class and then call back the overridden method at some point. This requirement may stem from the fact that the superclass does some set up operations that cannot be done in the subclass or if the subclass is expanding the superclass task rather than replacing it.
Calling a superclass method from a subclass is not in general a bad practice, but requiring it to do so is. Imposing such a constraint can lead to several problems. Future developers may forget to call the superclass causing untold bugs and system errors. Additionally, it requires anyone using the interface to have an understanding of the inner workings of the superclass. Ideally, they would only need to understand the public interface. Finally, if the superclass expects specific actions from the subclass, it may not perform well (or at all) if those actions aren't performed as expected.
A better approach to obtaining the desired functionality would be to use the Template Method pattern. Here the superclass would include a public method and define a separate method (often called a hook method) for the subclass to override. The superclass method would then call the hook method. The hook method can either be an abstract method in the superclass and fully implemented in the subclass, or have some basic functionality in the superclass and augmented in the superclass. Either way the subclass does not have to worry about calling the superclass.
Example
Let's say we have a class registration framework with an EventHandler superclass. The EventHandler is used to process all "transactions" - administrators adding classes, students registering for classes, students dropping classes, etc. It has to do some basic setup and housekeeping functions (checking availability, permissions, etc.) before the registration event can be processed. Our original code for a student registering for a class might be something like the following.
public class EventHandler ... public void handle (RegistrationEvent e) { setup(e); } public class StudentClassRegistrationHandler extends EventHandler... public void handle(RegistrationEvent e) { super.handle(e); RegisterStudent(e); }
The method StudentClassRegistrationHandler must call super.handle() before it can begin its task of registering the student. If we refactor this code using the Template Method pattern, we could get the following code:
public class EventHandler ... public void handle (RegistrationEvent e) { setup(e); doAction(e); } protected void doAction(RegistrationEvent e) { } public class StudentClassRegistrationHandler extends EventHandler ... protected void doAction(RegistrationEvent e) { RegisterStudent(e); }
The subclass is now only responsible for its own functionality. This arrangement also allows the superclass to call some follow-up or clean-up methods after the subclass method if necessary.
BaseBean
Description
Similar to the Call Super antipattern is the BaseBean antipattern. It is found in object-oriented programming when a concrete domain class is formed using inheritance from a utility class. This relationship is used simply to inherit utility methods from the utility class. This is sometimes referred to as inheritance for implementation.
Inheritance for the sake of gaining the functionality in the parent class is not good style. This obviously is not an "is-a" a relationship and may violate the Liskov Substitution Principle. By inheriting from the utility class, the domain class becomes dependent on the internals of the utility class. This can make the system difficult to maintain. Additionally, the domain class now has all the functionality of the utility class - some of which it might not need. This blurs the concept of the domain class and may cause it to have more than a single responsibility.
In good object-oriented programming, objects should be representative of the real-world entities they exemplify and should relate to each other as such. In this scenario a "has-a" relationship would be more appropriate. The inherited functionality can be obtained using delegation instead of inheritance. By using the composition over inheritance principle, we can avoid the BaseBean antipattern.
Example
Let's say we want to create a class that emulates a toll booth. The cars at the toll booth form a queue, so our toll booth will need the functionality (queue, dequeue, isEmpty, etc.) of a queue. We could implement our toll booth by inheriting this functionality from a Queue class as such:
public class TollBooth extends Queue{ /*additional methods and properties for toll booth such as toll booth operator, token processor, cross arm, change processor, etc. */
Obviously, a tollbooth is not a queue, but more appropriately, "has a" queue. Hence, our toll booth class should be created to contain a queue to model the line of cars.
public class TollBooth private Queue<Vehicles> /*additional methods and properties for toll booth such as toll booth operator, token processor, cross arm, change processor, etc. */
Our TollBooth class now has the functionality it needs without the inherent liabilities of extending the Queue class.
The Blob
Description
The Blob, also called a God Class, is a development antipattern that results when one single class has too many attributes, operations, or both.<ref name = sourcemaking_tb/> The Blob is usually an indicator of poor object-oriented design, or a poorly-migrated legacy program.<ref name = ap/> It can often resemble a procedural 'main' program, and may even encapsulate most or all of the functionality of an application. The Blob class violates the One Responsibility Rule, which makes it unlikely to be reusable. Because it has many responsibilities, it would be difficult to reuse in a future project. The Blob class may be expensive to load into memory, and wasteful if only part of the functionality is used. It also will likely be difficult to effectively test.<ref name = ap/> The Blob is typically caused by a lack of an object-oriented architecture.<ref name = ap/> It can also be the result of an up-front object-oriented design that did not take into account a requirement, and developers choosing not to rearrange the class hierarchy after the initial design. It can also be a Specified Disaster; the result of requirements that specify a procedural solution.<ref name = ap/>
The solution to The Blob is to refactor the code, with the goal of moving behavior away from the offending class.<ref name = sourcemaking_tb /> If The Blob encapsulates data in some other objects, then code manipulating that data should be moved to the other classes, in an effort to make the other classes more complex and The Blob less complex. If possible, the developers should try to split The Blob into multiple classes with class minimal coupling.
Example
An example of class approaching The Blob that may be familiar to many students this semester is the Expertiza Project's assignment model.<ref name="expertiza" /> The assignment model in Expertiza does not encapsulate the majority of functionality in the entire application, but it is out of proportion with most of the other classes, and it does include functionality that should be in some other classes.<ref name="fall2011_oss" /> One of the Open-Source Software projects for this semester is to correct some of these problems. The project assignment contains the following notes:<ref name="fall2011_oss" />
[The assignment model] contains functionality for adding and removing participants for this assignment, which should really be in a participant class, for assigning reviewers, which should probably be in a reviewer class, and for computing the maximum score possible on a questionnaire. Functions like compute_scores, and candidate_topics_to_review, among others, should be moved to other classes.
These notes describe some of the excessive functionality of assignment.rb. It also suggests some other classes that would be more appropriate locations for some functionalities. If these modifications are made, then the assignment model will be of a more manageable size, and will not be in danger of becoming The Blob.
Golden Hammer
Description
The Golden Hammer design AntiPattern, also known as the Law of the instrument, is an over-reliance on a familiar tool.<ref name="wiki_gh" /> It is one of the most common antipatterns seen in the industry.<ref name="sourcemaking_gh" />
Many software engineering projects solve problems or use techniques that programmers are strongly familiar with. Programmers often reuse strategies, algorithms, or entire sections of code that they or another programmer has applied to a different project in the past. Reuse in this manner can save development time and cost, but only when reused code is appropriate for the new application.
The Golden Hammer design anti-pattern results from reusing a familiar solution that is a poor match for a new problem.<ref name="sourcemaking_gh" /> This can occur if the developers are simply comfortable with or used to an existing approach, or even the result of narrow-mindedness or hubris. It can also be the direct result of reliance on proprietary technologies or products, or a deliberate effort to try to build a previous projects' success into a new program. A Golden Hammer will typically manifest itself with poor performance or scalability.<ref name="sourcemaking_gh" />
Example
When Automated Teller Machines (ATMs) were popularized in the 1970s, they were often designed using Dumb Terminals, a contemporary computer technology. In the 1990s and 2000s, many banks began replacing their aging ATMs with more modern ones that were physically more difficult to break into. Many of these banks have not changed the communication protocols that the ATMs use to conduct transactions with the bank's servers or mainframe, because weaknesses in the protocol or cryptography is not the cause of most ATM fraud.<ref name="crypto" /> As a result, many banks designed new ATMs that were required to somehow use the existing technology: encrypted dumb terminal sessions. The majority of these systems that have been deployed used a familiar Golden Hammer to do this: Terminal Emulator software installed on a variant of the Microsoft Windows operating system.<ref name="wikipedia_atm" /> This design decision has several negative effects, including:
- Increased unit cost of ATMs, because each one must have a licensed copy of a commercial operating system
- Concerns about the reliability and trustworthiness of the underlying ATM software<ref name=windowsatm />
Conclusion
In this article, we have described some of the most commonly occurring AntiPatterns. AntiPatterns comprise a set of defective processes and pitfalls in software development. They describe common errors in judgment or design made by both software engineers and project managers. They explain the causes of these errors, and offer suggestions for correcting the errors. AntiPatterns also define a standard set of vocabulary, so that they can be understood broadly. They are important for both programmers and managers to know and understand, in order to avoid costly mistakes.
Resources
For additional information regarding antipatterns, check out the following resources.
Books
- AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis. Brown, William J.; Raphael C. Malveau, Hays W. "Skip" McCormick, Thomas J. Mowbray, (ed) (1998).
- Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm], Ralph Johnson, and John Vlissides(the Gang Of Four)
Websites
- AntiPatterns.com Web site for the AntiPatterns book
References
<references> <ref name = koenig> Koenig, Andrew (March/April 1995). "Patterns and Antipatterns". Journal of Object-Oriented Programming 8 (1): 46–48.; was later re-printed in the: Rising, Linda (1998). The patterns handbook: techniques, strategies, and applications. Cambridge, U.K.: Cambridge University Press. p. 387. </ref> <ref name = gof> Design Patterns by the Gang of Four</ref> <ref name = ap> Brown, William J.; Raphael C. Malveau, Hays W. "Skip" McCormick, Thomas J. Mowbray, Theresa Hudson (ed) (1998). AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis. </ref> <ref name = sourcemaking_tb> http://sourcemaking.com/antipatterns/the-blob</ref> <ref name = wiki_gh> http://en.wikipedia.org/wiki/Golden_hammer </ref> <ref name = sourcemaking_gh> http://sourcemaking.com/antipatterns/golden-hammer </ref> <ref name = expertiza> Expertiza - Reusable learning objects through peer review </ref> <ref name = fall2011_oss> CSC/ECE517 Fall 2011 OSS Projects in Expertiza </ref> <ref name = crypto> Anderson, R. 1993. Why cryptosystems fail. In Proceedings of the 1st ACM Conference on Computer and Communications Security (Fairfax, Virginia, United States, November 03 - 05, 1993). CCS '93 [1]</ref> <ref name = wikipedia_atm> http://en.wikipedia.org/wiki/Automatic_Teller_Machine</ref> <ref name = windowsatm> "Technology News: Security: Windows Cash-Machine Worm Generates Concern". Technewsworld.com </ref> </ref> </references>