Design Patterns

Introduction

Design Patterns are an important power tool in your toolbox. You will fall back on these trusty patterns again and again, knowing that you can depend on their robustness.

But how many are there, and which ones do I need? That’s individual, but here is my list.

You can roughly divide the patterns into groups of types:

  • Creational
  • Structural
  • Behavioural.

I have listed the most common in no  particular order.

Creational patterns

Factory

A factory class creates concrete implementations of an interface. Easy negative lookahead. Used when you want to separate the creation of objects.

Abstract Factory

Same as Factory, but the Factory itself is abstract

Singleton

Debatable whether it should be used, but it assures only one instance of a class. Normally a static getInstance() method.

Builder

Gives you a flexible way of building complex objects without a complex constructor!  Every setXXX() method returns”this” so you can chain the calls.

instead of something like

new Meal("Burger",0,0,0,null,null,0,5,2,3);

you get

Meal m = MealBuilder.type("Burger").bread().salad().tomato().build();

Dependency Injection

You don’t create the objects yourself, but accepts them from an external “injector”. Thereby you let the client decide which implementation to use.

Normally you do this by accepting an interface as argument, and letting the user or framework decide exactly which implementation you should use.

Lazy Initialization

Similar to Proxy. Delay init, or calculation of expensive process until first time it is actually needed. A method, maybe a getValue(), will check whether the value is actually calculated and do it if necessary.

Prototype

With prototype, changes to the Parent object will affect all child instances. When you create a child object, you actually clone the parent one.

Structural Patterns

Adapter

A bridge between incompatible interfaces.

Motivation: After the application is designed, convert an existing interface into another interface.

When to use:

  • When an outside component provides captivating functionality that we’d like to reuse, but it’s incompatible with our current application. A suitable Adapter can be developed to make them compatible with each other
  • When our application is not compatible with the interface that our client is expecting
  • When we want to reuse legacy code in our application without making any modification in the original code

That said, if you can extend the interface, do that instead! An Adapter is an ugly patch.

Example

public class Car {

}

public interface CarSpecs {
  public double getSpeed();
}

// and an implementation

public class BugattiVeyron extends Car implements CarSpecs {
@Override
  public double getSpeed() {
    return 268; // Miles per Hour
  }
}

Now a client wants the speed in KM/H, but we cant change the interface, so we add an adapter

public interface CarSpecsEuro {
    // returns speed in KM/H 
    double getSpeed();
}

and implement the adapter

public class CarSpecsEuroAdapter implements CarSpecsEuro {
  private Car car;

  // standard constructors
  CarSpecsEuroAdapter(Car car) {this.car = car;}

  @Override
  public double getSpeed() {
    return convertMPHtoKMPH(car.getSpeed());
  }

  private double convertMPHtoKMPH(double mph) {
    return mph * 1.60934;
  }
}

Then use the adapter:

Car bugatti = new Bugatti();
bugatti.getSpeed();

CarSpecsEuroAdapter bugattiEuroAdapter = new CarSpecsEuroAdapter(bugatti);

// use bugattiAdapter:

bugattiEuroAdapter.getSpeed();

Bridge

Decouples compile-time binding of an abstraction from its implementation. Bridge is Better than Adapter (Afterthought).

Filter/Criteria

A generic way of filtering objects based on one or multiple criterias.

Composite

When an instance of a class (Container) can contain Containees, each of which could be a Container.  Container and Containee should share an interface to make them interchangeable. Can be used for Person with children that are a list of Person.

Decorator

Add functionality without subclassing.

When you decorate an object, you can only call the public methods on that object. When you subclass, you can call any method, even protected ones. That can make subclasses break more frequently, because they can accidentally rely on their parents’ implementation details. Those details will usually change more often than the public methods.

Decorators can be especially useful when you’re breaking apart large classes. With decorators, it’s easier to follow the Single-Responsibility Principle – It hides the complexities of the system and provides an interface to the client which the client can access the system. Each decorator can do one thing and do it well, and you can combine decorators to get more complex behavior.

Example:

Facade

Hides the complexity of a system by providing a simple interface.

Flyweight

Reduces the number of objects created. Decreases memory footprint. Reuses existing objects in cache instead of creating new ones.

Proxy

A class that represents (by proxy) the functionality of another class. Can delay creation of “real” object until its absolutely necessary.

 

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.