Brief introduction to Design Patterns

data: 5 września, 2014
czas czytania: 3 min
autor: Szymon Szymański

Design patterns are the ready descriptions giving solutions to repetitive and typical project problems.

They are the results of experience, hard work and a great number of trials and errors. They also represent considered and best practices of object-oriented programming (SOLID, DRY, KISS and YAGNI). The patterns became an element of communication among programmers and they are an element of every engineer’s primer.

In general, design patterns are nothing else than walkthroughs for repetitive problems, which were worth writing down and understanding. It is worth noticing that design patterns don’t suggest a direct implementation of a solution, but are only an outline of a structure with some characteristic aspects that should be adapted to our needs.
The very notion of design patterns was introduced by Christopher Alexander in 1977. Then , in 1987, Kent Beck and Ward Cunningham gave life to patterns of modern computer science. Several years passed and in 1994 the Gang of Four has published „Design Patterns: Elements of Reusable Object-Oriented Software”. The work of Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides is constantly helping developers in introducing best practices.

Imagine the situation in which a fellow programmer tries to explain to you how a flow between widgets and a class communicating through SOAP works. Easier and definitely faster would be to say „my walkthrough uses observer pattern”.

It is worth noticing that there is no homogeneous division of design patterns and, usually, there are as many of them as studies on this subject. For the purpose of this article we use the division proposed by the Gang of Four:

  • Creational patterns
  • Structural patterns
  • Behavioural patterns

Each design pattern is suitable for a determined situation. Used in an improper scenario they may do more harm than good!
In this article, we will limit ourselves to selected patterns which will represent given categories proposed by the Gang of Four.

Creational Patterns

Creational patterns are associated with control mechanisms of creating objects. The basic mode of forming an object may be problematic in some projects and may lead to unnecessary complexity in some areas. Creational patterns are supposed to prevent from occurring problems and introduce more control over creating objects. Their task is to separate the processes of creation, completion and representation of an object.

There are five well-known design patterns possible to implement in a wide scope of programming languages:

  • Abstract Factory Pattern
  • Builder Pattern
  • Factory Method Pattern
  • Prototype Pattern
  • Singleton Pattern

In this article we will take a closer look at factory and singleton methods.

The Singleton pattern

The Singleton pattern provides at most one instance of a given class and global access to its references. Very often is presented as an anti-pattern as it breaks two SOLID rules (single responsibility principle and open/closed principle), sometimes it is overused and very often is treated as object substitute of a global variable. It also happens to be problematic during implementation in multi-threaded environment.

Use this pattern if your application requires global access to the same resource.

design_patterns_singleton

Below you can find an exemplary implementation of a singleton in CiviCRM 3.4:

_session =& $_SESSION;
        $this->create();
    }

    static function &singleton()
    {
        if (self::$_singleton === null ) {
            self::$_singleton = new CRM_Core_Session;
        }
        return self::$_singleton;
    }
// other methods
}

The Factory Method Pattern

The Factory Method pattern is one of the best known design patterns.

Its task is to hide details of creating objects from clients and, at the same time, provide interface to generate them. Classes implementing interface decide about class of creating object.

A client expects a secured implementation of an interface or abstract class, but is not interested in a specific implementation. However, a programmer can easily expand the portfolio of a factory with additional object classes.

When designing your application it is worth considering whether and application actually needs the objectives factory. If a product has many classes with a similar base class and you manipulate objects in the interface or abstract class, then Factory Method Patter would definitely be useful.

design_patterns_factory_method

Below an example of implementation in PHP:

produce('adults');
$robot2 = $factory->produce('kids');
echo $robot1->doAction() . "
" . $robot2->doAction(); /** Console output: Delivering your pizza! Beep Beep Pew Pew */

The Abstract Factory Pattern

The Abstract Factory Pattern goes a step further and moves to a higher level of abstraction (hence the name). In this case, a client has no clue what kind of factory is used. Abstract factory provides an interface for creating groups of dependent objects, which are used by the client, without giving their exact implementation.

For the creation of specific products, which are available for the user-defined interface, factories based on Factory Method Pattern or the Prototype Pattern are often used (which is not discussed here).

The main difference between Factory Method Pattern and Abstract Factory Pattern is the fact that in the former a client receives one product, whereas in the latter a client gets the whole group of products.

Below presented diagram will facilitate understanding of the Abstract Factory concept:

design_patterns_abstact_factory

/**
Abstract First Class Implementations
*/
abstract class AbstProduct1{
	public abstract void action();
}

class Product1A extends AbstProduct1{
	Product1A(String arg){
		System.out.println("I'm "+arg);
	}
	public void action() { };
}

class Product1B extends AbstProduct1{
	ProductA2(String arg){
		System.out.println("I'm "+arg);
	} 
	public void action() { };
}

/**
Abstract Second Class Implementations
*/
abstract class AbstProduct2{
}

class Product2A extends AbstProduct2{
	Product2A(String arg){
		System.out.println("I'm "+arg);
	} 
}

class Product2B extends AbstProduct2{
	ProductB2(String arg){
		System.out.println("I'm "+arg);
	}
}

/**
Abstract Factory Implementation
*/
abstract class AbstractFactory{
	abstract AbstProduct1 createProduct1();
	abstract AbstProduct2 createProduct2();
}

class FactoryA extends AbstractFactory{
	AbstProduct1 createProduct1(){
		return new Product1A("Product1A");
	}
	AbstProduct2 createProduct2(){
		return new Product2A("Product2A");
	}
}

class FactoryB extends AbstractFactory{
	AbstProduct1 createProduct1(){
		return new Product1B("Product1B");
	}
	AbstProduct2 createProduct2(){
		return new Product2B("Product2B");
	}
}

// Some factory action
class FactoryMaker{
	private static AbstractFactory pf=null;
	static AbstractFactory getFactory(String choice){
		if(choice.equals("a")){
			pf=new FactoryA();
		}else if(choice.equals("b")){
				pf=new FactoryB();
			} return pf;
	}
}

// Client side
public class ClientApp{
	public static void main(String args[]){
		AbstractFactory pf=FactoryMaker.getFactory("a");
		AbstractProductA product=pf.createProduct1();
	}
}

Behavioural Patterns

Behavioural patterns task is to introduce flexibility to solutions connected with inter-objects communication. They are focused on allocating specific roles and duties between objects in communication.

This kind of patterns are:

  • Iterator Pattern
  • Observer Pattern
  • Command Pattern
  • Strategy Pattern
  • Template Method Pattern

For the purpose of this article we will discuss Observer and Strategy Patterns.

Observer Pattern

If there is a need to inform the group of objects to change the state of another specific object, this pattern would be the most appropriate solution. A one to many relation is introduced when the observed object changes its internal state, then all its observers are automatically informed about this change.

It’s programmer decision concerning how a given information about the status change is transferred. There are two methods of doing this:

  • Push – an observed object transfers information to its observers including information concerning status change. This method is not recommended when an observed object handles a massive number of data.
  • Pull – observers’ object pulls important information on its own from the observed object after receiving the information concerning status change. This method can be ineffective and troublesome in a multithreaded environment.

design_patterns_observer

An example of implementation in PHP:

getAnnouncement() . "
"; } } class Newsletter extends Subject { private $favoritePatterns = NULL; private $observers = array(); function __construct() { } function register(Observer $observer) { $this->observers[] = $observer; } function detach(Observer $observer) { foreach($this->observers as $key => $value) { if ($value == $observer) { unset($this->observers[$key]); } } } function notifyAll() { foreach($this->observers as $observer) { $observer->update($this); } } function updateAnnouncement($announcement) { $this->announcement = $announcement; $this->notifyAll(); } function getAnnouncement() { return $this->announcement; } } $news = new Newsletter(); $oldAgeReader = new Reader(); $news->register($oldAgeReader); $news->updateAnnouncement('Extra Extra Read all about it!'); $news->updateAnnouncement('There is nothing interesting today'); $news->detach($oldAgeReader); $news->updateAnnouncement('Foo bar baz');

Strategy Pattern

Sometimes, classes are different only when it comes to behaviour, and in this case it is worth isolating those parts of class that are changeable and replace them when the program works.

Strategy Pattern works in the following way; it isolates algorithms, encapsulates them and makes them changeable. It is one of the easier patterns to implement.

UML diagram and an example of implementation in Java:

design_patterns_strategy

An example in Java:

public interface TextAction {
  public String doAction(String bit);
}

public class TextScreamer implements TextAction {
  @Override
  public String doAction(String bit) {
    return bit.toUpperCase();
  }
}

public class TextWhisperer implements TextAction {
  @Override
  public String doAction(String bit) {
    return bit.toLowerCase();
  }
}

public class TextModification {
  public TextModification(TextAction a) {
    this.fAction = a;
  }

  @Override
  public void manipulate() {
    StringTokenizer t = new StringTokenizer(processedString);
    while (t.hasMoreTokens()) {
      System.out.print(fAction.doAction(t.nextToken(" ")));
      System.out.print(" ");
    }
    System.out.println();
  }

  public TextAction getAction() {
    return fAction;
  }

  public void setAction(TextAction fAction) {
    this.fAction = fAction;
  }

  public String getProcessedString() {
    return processedString;
  }

  public void setProcessedString(String processedString) {
    this.processedString = processedString;
  }

}

public static void main(String[] args) {
    TextWhisperer m = new TextWhisperer();
    TextScreamer s = new TextScreamer();
    TextModification c = new TextModification(m);
    c.setProcessedString("Big queue!");
    c.manipulate();
    c.setAction(s);
    c.setProcessedString("Almost there!");
    c.manipulate();
}

Structural patterns

The most important feature of these patterns is to facilitate the operation and design applications through finding an easy way to realize dependencies between entities. Due to this patterns it is easier to design applications which contain independent class libraries.

The following structural patterns are one of the best well-known ones:

  • Adapter Pattern
  • Decorator Pattern
  • Facade Pattern
  • Proxy Pattern
  • Composite Pattern

At the end we will look closer at the Facade and Adapter Patterns.

Facade Pattern

Facade Pattern is a simple design pattern, which provides a simplified interface for the larger piece of code. It makes code more legible and comfortable to use by stitching intricate references to the subsystem or library sub functions.

This pattern may also be used to give remarkably better interface to purely designed API. It allows for more convenient control over subsystem designed under facade.

The following example would be the best way to understand the idea of Facade Pattern:

public class Factory1 {
	public void make(){
		// here should be some serious stuff
	}
}
public class Factory2 {
	public void produce(){
		// here should be some serious stuff
	}
}
public class Factory3 {
	public void create(){
		// here should be some serious stuff
	}
}

public class FacadeExampleClass{
	private Factory1 f1;
	private Factory2 f2;
	private Factory3 f3;
	public FacadeExampleClass(){
		f1 = new Factory1();
		f2 = new Factory2();
		f3 = new Factory3();
	}
	void triggerAll(){
		f1.make();
		f2.produce();
		f3.create();
	}
}
(...)
public static void main(String[] args) {
	FacadeExampleClass c = new FacadeExampleClass();
	c.triggerAll();
}

Adapter Pattern

Adapter Pattern seems to be similar to Facade Pattern but its task is to introduce a new, awaited by the client, interface which hides the previous one. Due to adaptation the communication between given classes, that normally would not communicate with each other because of the incompatible interface, is now possible.

design_patterns_adapter

An example of implementation in JUnit4 framework:

// imports

public class JUnit4TestAdapter implements Test, Filterable, Sortable, Describable {
    private final Class fNewTestClass;

    private final Runner fRunner;

    private final JUnit4TestAdapterCache fCache;

    public JUnit4TestAdapter(Class newTestClass) {
        this(newTestClass, JUnit4TestAdapterCache.getDefault());
    }

    public JUnit4TestAdapter(final Class newTestClass, JUnit4TestAdapterCache cache) {
        fCache = cache;
        fNewTestClass = newTestClass;
        fRunner = Request.classWithoutSuiteMethod(newTestClass).getRunner();
    }

    public int countTestCases() {
        return fRunner.testCount();
    }

    public void run(TestResult result) {
        fRunner.run(fCache.getNotifier(result, this));
    }

// other methods
}

Summarising, we got to know a few design patterns and their features suggested by the Gang of Four. Above presented examples are a good base for further expansion of knowledge in a design patterns field. The best way of acquiring the knowledge concerning patterns are the following books: Design Patterns Elements of Reusable Object-Oriented Software, Pattern-Oriented Software Architecture vol. 1-5 and Patterns of Enterprise Application Architecture. It is also worth following the activities of the community working with those patterns: The Hillside Group, Cunningham & Cunningham.

And you, what kind of patterns do you mostly encounter?

Newsletter IT leaks

Dzielimy się inspiracjami i nowinkami z branży IT. Szanujemy Twój czas - obiecujemy nie spamować i wysyłać wiadomości raz na dwa miesiące.

Subscribe to our newsletter

Administratorem Twoich danych osobowych jest Future Processing S.A. z siedzibą w Gliwicach. Twoje dane będziemy przetwarzać w celu przesyłania cyklicznego newslettera dot. branży IT. W każdej chwili możesz się wypisać lub edytować swoje dane. Więcej informacji znajdziesz w naszej polityce prywatności.

Subscribe to our newsletter

Administratorem Twoich danych osobowych jest Future Processing S.A. z siedzibą w Gliwicach. Twoje dane będziemy przetwarzać w celu przesyłania cyklicznego newslettera dot. branży IT. W każdej chwili możesz się wypisać lub edytować swoje dane. Więcej informacji znajdziesz w naszej polityce prywatności.