CSC/ECE 517 Fall 2009/wiki3 1 ab

From Expertiza_Wiki
Jump to navigation Jump to search

Antipattern

An AntiPattern describes a commonly occurring solution to a problem that generates negative consequences. The term was coined in 1995 by Andrew Koenig, inspired by Gang of Four's book Design Patterns, which developed the concept of design patterns in the software field. Antipatterns are documented in detail nowadays in software design because identifying bad practices can be as valuable as identifying good practices. A well formulated AntiPattern also tells us why the bad solution looks attractive (e.g. it actually works in some narrow context), why it turns out to be bad, and what positive patterns are applicable in its stead. As an example, consider braking in a car (in the days before antilock brakes): if the conditions involve ice, solving the "I must stop" problem by firmly depressing the brake would be an AntiPattern: it seems like a good idea, but it has a fatal flaw in that firm braking on ice causes skidding and little deceleration. Studying the AntiPattern teaches you to pump the brake, a more appropriate solution Pattern.

AntiPatterns identify and categorize the common mistakes in software practice, which is a target-rich environment for research in AntiPatterns . AntiPatterns also identify alternative solutions and extend the field of design patterns research into exciting new areas and issues, including: refactoring, reengineering, system extension, and system migration.

When Antipatterns are used

Anti-patterns come into being mostly when

  • Not using any design patterns during design or coding, though this will work at the beginning but doubtful if it will withstand the test of times.
  • Time becomes the biggest constraint. Agile methodologies dominating the projects arena in the recent times, tend to leave design to the wayside entirely and that has become the popular belief for a proper approach. This fits in perfectly with management’s request of trying to get things done right now no matter what. Refactoring becomes a much bigger part of the process through iterations. However, not attempting to recognize components and apply patterns where applicable before “diving in” and just getting it coded can very negatively effect the project’s long term success.
  • Patterns are over used and the situation does not demand it. Just like lack of design patterns usage is an antipattern, overusing patterns is an antipattern too. Extensive use of patterns, when there is not a need makes the code nowhere near maintainable. This in turn, conflicts one of the most important goals of design patterns, code maintainability.


Types of Software design anti-patterns

  • Abstraction Inversion

  • Problem: Abstraction Inversion is an anti pattern where a complicated mechanism is used to build a simple abstraction, when it would be possible to do things other way around.

    Example: Abstraction Inversion arises when users of a construct need functions implemented within it but not exposed by its interface. The result is that the users re-implement the required functions in terms of the interface, which in its turn uses the internal implementation of the same functions.

    Solution:

    For designers of lower-level software:
    • If the system offers formally equivalent functions, choose carefully which to implement in terms of the other.
    • Do not force unnecessarily weak constructs on the users.

    For implementers of higher-level software:

    • Choose the infrastructure carefully.
  • Ambiguous Viewpoint

  • Problem: The viewpoint represented by the Object oriented Analysis and Design models are usually presented without any clarifications. These models denote an implementation viewpoint that visualizes the structure of a computer program. Mixed viewpoints do not support the fundamental separation of interfaces from implementation details.

    Example: Defining a class model for a telephone exchange system will vary significantly depending upon the focus provided by the following perspectives: Telephone user, who cares about the ease of making calls and receiving itemized bills.
    Telephone operator, who cares about connecting users to required numbers.
    Telephone accounting department, which cares about the formulae for billing and records of all calls made by users.

    Solution: There are three fundamental viewpoints for OOA&D models: the business viewpoint, the specification viewpoint, and the implementation viewpoint.

    The business viewpoint defines the user’s model of the information and processes.

    The specification viewpoint focuses on software interfaces. Because objects (as abstract data types) are intended to hide implementation details behind interfaces, the specification viewpoint defines the exposed abstractions and behaviors in the object system. The specification viewpoint defines the software boundaries between objects in the system.

    The implementation viewpoint defines the internal details of the objects. Implementation models are often called design models in practice. To be an accurate model of the software, design models must be maintained continuously as the software is developed and modified.

  • Big Ball of Mud

  • Problem: A haphazardly structured code or program is referred to as a Big Ball Of Mud . The properties of such a code or program are as follows.
    1. Undefined Structure
    2. Unregulated Growth
    3. Expedient Repair
    4. Important information is shared throughout the system to such an extent that they become global or duplicated.

    Example: Building sytems on code which was written as a quick fix(throw away code), which is meant to be used once rather than designing a proper, general program from the ground up is an example of Big Ball of Mud.

    Solution: To control such a Big Ball Of Mud projects, programmers are strongly encouraged to study it and to understand what it accomplishes, and to use this as a loose basis for a formal set of requirements for a well-designed system that could replace it.

  • The Gas Factory or Unnecessary Complexity

  • Problem: In a system, when implementing some structure, we always try to make it as generic as possible to make it easy to later reuse those parts. There is normal complexity when the code is built and as it continues , complexity adds up.

    Example: consider building few collections and one of the collection need a special feature. The problem is when this functionality is extracted to try and make it as generic as possible so that anyone could reuse your class. That is the problem of the unnecessary complexity. Making the stuff generic for a single class, structures are added inside the code. Some of those structure might be proof tested for this specific class but might fail on other classes. Another problem is that maybe no other class will ever require this functionality.

    Solution: This anti pattern can be avoided by following YAGNI and Lean Software Development, you delay code and unnecessary complexity until you actually require the complexity. For example, If you have 2 classes that require the same functionality, extract this functionality inside a different class and make those 2 classes inherit from it.

  • Functional Decomposition

  • Problem: This AntiPattern is the result of experienced, nonobject-oriented developers who design and implement an application in an object-oriented language. When developers are comfortable with a “main” routine that calls numerous subroutines, they may tend to make every subroutine a class, ignoring class hierarchy altogether (and pretty much ignoring object oriented design entirely).

    Examples: Classes with “function” names such as Calculate_Interest or Display_Table may indicate the existence of this AntiPattern.
    All class attributes are private and used only inside the class.
    Classes with a single action such as a function.

    Solution: Ascertain what the basic requirements are for the software, define an analysis model for the software, to explain the critical features of the software from the user’s point of view. For each step provide detailed documentation of the processes used as the basis for future maintenance efforts.Formulate a design model that incorporates the essential pieces of the required system. Do not focus on improving the model but on establishing a basis for explaining as much of the system as possible.

Object-oriented design anti-patterns - A subset of Software design of Anti patterns

A design anti-pattern in an object-oriented program, just like in software design, is an example of design that initially appeared to be a good idea, but later turned out to be a bad one. Anti-patterns are examples of bad design and bad coding practice. Some examples of object-oriented anti-patterns:

  1. Creating massive classes with a huge number of methods.
  2. Too many utility classes that perform too many operations. A utility class is one that has only static methods, no state and which performs operations on objects that are passed in.
  3. Performing excessive and unnecessary run-time type checks, rather than using the type system to do the work

Anti patterns are very many since there are numerous ways to do something 'the wrong way'. Few of the object oriented design anti-patterns are discussed below.

Types of Object-oriented design anti-patterns

  • Anemic Domain Model

  • Problem: This pattern proposed by Martin Fowler, arises when the logic is typically implemented in separate classes, which transform the state of the domain objects. With the anemic domain model, all the logic is not given with the associated object. It’s located in the objects that use them. So the problem is unless you are using the objects that have the behavior, having the anemic domain model does not do any good. In fact, they just getters and setters with barely enough behaviour to call them objects. So in short, it looks like a model, it smells like a model but there is no behavior inside. It brings in problems like code duplication and maintenance, since the business logic is spread across the business, all the common business logic will need to be updated all at once and validated against their respective service and validation.

    Example: A class with a private variable and availability of no code to persist the variable's state.

    class Account {
    private double balance;
    public double getBalance() {
    return balance;
    }
    public void setBalance(double balance) {
    this.balance = balance;
    }
    class AccountService {
    public void debit(Account account, double amount) {
    account.setBalance(account.getBalance() - amount);
    }
    public void credit(Account account, double amount) {
    account.setBalance(account.getBalance() + amount);
    }
    }

    As shown above the object Account acts just as a place holder for “balance”. In this model most probably some kind of service object (AccountService) sets and gets the value of the balance to reflect any changes to the balance. This is very luring because its pretty straight forward. What makes this Anemic is there is no behavior associated with this Account object itself.

    Solution: The business logic has to go inside your domain model and made easy to understand. Still, if a certain part of the business needs “special” behavior, it will have to be incorporated inside the main domain model such that objects can self validate since the validation logic is located inside of the object.

  • BaseBean

  • Problem: In object-oriented programming, a BaseBean is a utility object from which concrete entites are derived (via subclassing). Using inheritance causes the derived class to rely on the internals of a base class which may be out of the control of the developer. While the utility classes are typically stable and fairly similar across implementations, some innocuous change in the future could break the derived class (since the relationship is not well defined). In addition, it muddies the business meaning of the derived class.

    Example: A class ComboBox, whose functionality needs to be extended and is done by creating a subclass of ComboBox and adding/overriding appropriate methods.

    Solution: A class should not inherit from another class simply because the parent class contains functionality needed in the subclass. Instead, delegation (has-a relationship) should be used to obtain the business logic or data structure that is required. In technical terms, this case warrants composition over inheritance.

  • Call Super

  • Problem: Call super requires users of a particular interface to override the method of a superclass and call the overridden from the overriding method. This usage brings in two major problems.

    • 'super' method has to be called or the program cannot be implemented. That is a very serious problem and of course, IDE's can help by providing templates for these methods, but there's no standard way of doing this and no enforcement from the underlying VM.
    • The other problem arises when a call to the 'super' method fails or an exception is thrown thereby breaking the call to the subsequent methods in the chain of command.

    Example:

    public class EventHandler ...

     public void handle (BankingEvent e) {
       housekeeping(e);
     }
    

    public class TransferEventHandler extends EventHandler...

     public void handle(BankingEvent e) {
       super.handle(e);
       initiateTransfer(e);
     }
    

    Solution: A better approach to solve these issues is instead to use the template method pattern, where the superclass includes a purely abstract method that must be implemented by the subclasses and have the original method call that method.

    How to avoid the CallSuper anti-pattern

    Here the common code would go into the abstract method of the abstract class and it would act like a container for all the other methods that need to be used by the subclasses.

  • God object

  • Problem: In object-oriented programming, a God object is an object that knows too much or does too much. Concentrating too many methods in a single class makes it the only point of contact for other classes, which want to use one of its functionalities. Instead of objects communicating amongst themselves directly, the other objects rely on the God object. Because the God object is referenced by so much of the other code, maintenance becomes more difficult than it otherwise would.

    Example: A class by name 'ConfigParameters' does all the functions of getting the parameters from the user and setting them in a config file to the appropriate variables and is called in several parts of the program for reading config variables.

    Solution: The best way this anti pattern is avoided is by breaking a big problem into many smaller problems (divide and conquer) and solutions are created for each of them. If you are able to solve all of the small problems, you have solved the big problem as a whole. Therefore there is only one object about which an object needs to know everything: itself. And there is only one problem an object needs to solve: its own.

  • Singletonitis

  • Problem: Overuse of the singleton pattern is referred to as Singletonitis. The major problem with the Singleton pattern is that it creates far too many dependencies within a system. When more and more classes depend on a singleton you start to end up with spaghetti code which is hard to read, even harder to debug and pretty much impossible to unit test.

    Example: getInstance() method used to get the instance of the Singleton class.

    Solution: A good solution to this problem would be to write loosely coupled, testable and reusable components. Patterns such as Inversion of Control and the containers which implement this provide excellent alternatives to the singleton.

  • Sequential Coupling

  • Problem: A class suffers from sequential coupling when the consumer of the class must call the methods in a particular sequence or suffer the consequences. This anti-pattern requires an interface that forces you to call multiple methods in the particular order when the interface isn't inherently stateful (like a Collection). In some of the scenarios, the implementation might be storing state in a way that isn't clearly indicated by the interface.

    Example: java.util.MessageFormat which uses temporary internal state during single method calls.

    public interface InterfaceAnalyzer {
        public void setOptions(AnalyzerOptions options);
        public void setText(String text);
        public void initialize();
        public int getOccurrences(String query);
    }
    
    InterfaceAnalyzer analyzer = AnalyzerFactory.getAnalyzer();
    analyzer.setOptions(true);
    analyzer.initialize();
    Map<String, Integer> results = new HashMap<String, Integer>();
    for(String item : items) {
        analyzer.setText(item);
        results.put(item, analyzer.getOccurrences);
    }

    This implementation is considered to be ineffective because as indicated above, it is storing state in a way that is not explicitly shown by the interface.

    Solution: This might be an anti pattern depending upon the context. In some cases, refactoring might help avoiding the expensive analysis on the data.

  • Yo Yo Problem

  • Problem: The yo-yo problem is an anti-pattern that occurs when a programmer has to read and understand a program whose inheritance graph is so long and complicated that the programmer has to keep flipping between many different class definitions in order to follow the control flow of the program. It often happens in object-oriented programming. The term comes from comparing the bouncing attention of the programmer to the up-down movement of a toy yo-yo.

    Example: Any API that has inheritance hierarchy. With methods overridden from the parent class and new methods added to the child class.

    Solution: It is proposed to keep the inheritance graph as shallow as possible, in part to avoid this problem. The use of composition instead of inheritance is also strongly preferred, although this still requires that a programmer keep multiple class definitions in mind at once. Techniques such as documenting layers of the inheritance hierarchy can reduce the effect of this problem, as they collect in one place the information that the programmer is required to understand.



Links