Software Design Patterns

Florian Rappl, Fakultät für Physik, Universität Regensburg

Software Design Patterns

Introduction to modern software architecture

software architecture

Creational patterns

Introduction

  • Deal with object creation
  • Try to control the object creation process
  • Basically two intentions are often seen here:
    1. Making systems independent of specific classes
    2. Hiding how classes are created and combined
  • Goal: Minimizing dependencies and complexities while maximizing flexibility

The singleton pattern

  • Restricts the instantiation of a class to one object
  • Useful if only one instance is needed and this instance is used by a lot of objects
  • Much better controlled than global variables
  • Encapsulates the given set of variables
  • Can be resource friendly by allocating memory when required, not before
  • Required: Private or protected constructor and static readonly property to access instance

Singleton diagram

skinparam classAttributeIconSize 0
class Singleton {
  -{static} instance: Singleton = null
  +{static} getInstance(): Singleton
  -Singleton()
}

Remarks

  • There are multiple variants - the shown one is called lazy initialization and represents the most common approach
  • By using a static constructor we could have an eager initialization and avoid the unnecessary condition in the property
  • Therefore there is a way that uses both approaches to have the advantages of lazy loading without any repeating condition testing
  • Another advantage of this solution is that it would be thread-safe (the original lazy is not thread-safe)

Lazy loading singleton

public class Singleton
{
   private static Singleton instance;

   private Singleton() {}

   public static Singleton Instance
   {
      get 
      {
         if (instance == null)
            instance = new Singleton();

         return instance;
      }
   }
}
public class Singleton {
   private static Singleton instance;

   private Singleton() { }

   public static Singleton getInstance() {
      if(instance == null)
         instance = new Singleton();

      return instance;
   }
}
class Singleton {
   public:
   static Singleton& getInstance() {
      static Singleton instance;
      return instance;
   }

   private:
   Singleton() {}
   Singleton(Singleton const&);
   void operator=(Singleton const&);
};

Practical considerations

  • Quite often used together with other patterns
  • Central location for variables (like a namespace), but more clean and encapsulated
  • Can be controlled quite easily (getInstance could have more logic)
  • Configuration of application stored in a Singleton allows easy and automated serialization and deserialization of stored values
  • APIs make great use of singletons to provide a single entry point
  • Enhance a resource friendly way of using particular objects

Global application state

public sealed class ApplicationState
{
   private static ApplicationState _instance;
   private static object _lockThis = new object();

   private ApplicationState() { }

   public static ApplicationState Instance
   {
      get
      {
         if (_instance == null)
         {
            lock (_lockThis)
            {
               if (_instance == null)
                  _instance = new ApplicationState();
            }
         }
         return _instance;
      }
   }

   public string LoginId { get; set; }

   public int MaxSize { get; set; }
}

The prototype pattern

  • Use exchangeable objects as parts of a class, instead of deriving from another class
  • Big advantage: We do not have to destroy the complete object to change the parent (which is now the prototype)
  • Some languages are actually using this pattern implicitly
  • The creation of the prototype is usually done by calling a cloning method on an already existing instance
  • The cloning process should result in a deep copy of the prototype

Prototype diagram

skinparam classAttributeIconSize 0
class Client {
  -prototype : Prototype
}

interface Prototype {
  +clone() : Prototype
}

class ConcretePrototype1 {
  +clone() : Prototype
}

class ConcretePrototype2 {
  +clone() : Prototype
}

class ConcretePrototypeN {
  +clone() : Prototype
}

Client *-> Prototype
ConcretePrototype1 --|> Prototype
ConcretePrototype2 --|> Prototype
ConcretePrototypeN --|> Prototype

Remarks

  • Cloning is an important part, but it should not be the only reason to follow the prototype pattern
  • .NET and Java have already a single purpose interface for cloning alone
  • The main reason for this pattern is flexibility
  • Prototype doesn't require subclassing, but an initialize operation
  • One instance of a class is used as a breeder of all future instances
  • Prototype is unique among the other creational patterns since it only requires an object, not a class

Base class prototype

abstract class Prototype
{
    private readonly string _id;

    protected Prototype(string id)
    {
        _id = id;
    }

    public string Id
    {
        get { return _id; }
    }

    public abstract Prototype Clone();
}

class ConcretePrototype : Prototype
{
    public ConcretePrototype(string id)
        : base(id)
    {
    }

    public override Prototype Clone()
    {
        return (Prototype)MemberwiseClone();
    }
} 
abstract class Prototype {
    private final string _id;

    protected Prototype(string id) {
        _id = id;
    }

    public string getId() {
        return _id;
    }

    public abstract Prototype clone();
}

class ConcretePrototype extends Prototype {
    public ConcretePrototype(string id) {
        super(id);
    }

    @Override
    public Prototype clone() {
        return (Prototype)Object.clone(this);
    }
} 
class Prototype {
    private:
    const string _id;

    protected:
    Prototype(string id) : _id(id) {
    }

    public:
    const string& getId() const {
        return _id;
    }

    virtual Prototype* Clone() = 0;
};

class ConcretePrototype : public Prototype {
    public:
    ConcretePrototype(string id) : Prototype(id) {
    }

    virtual Prototype* Clone() {
        return new ConcretePrototype(*this);
    }
};

Practical considerations

  • The prototype pattern can save resources by cloning only certain parts and omitting some initialization
  • Usually we will get a general object back, so casting might be required
  • Designs that make heavy use of the other patterns (e.g. composite, decorator, ...) often can benefit from prototype as well
  • A Singleton that contains all prototype objects is quite useful
  • The singleton would then have a method to create (clone) a specific prototype

Changing a template

public abstract class Template
{
    public abstract Template Copy();

    public Color Foreground { get; set; }

    public Color Background { get; set; }

    public string FontFamily { get; set; }

    public double FontSize { get; set; }

    /* ... */

    protected static void CopyBasicFields(Template original, Template copy)
    {
        copy.Foreground = original.Foreground;
        /* ... */
    }
}

public class DefaultTemplate : Template
{
    public DefaultTemplate()
    {
        Foreground = Color.FromRgb(0, 0, 0);
        Background = Color.FromRgb(255, 255, 255);
        FontFamily = "Arial";
        FontSize = 12.0;
    }

    public override Template Copy()
    {
        DefaultTemplate copy = new DefaultTemplate();
        CopyBasicFields(this, copy);
        /* ... */
        return copy;
    }
}

public class Document
{
    Template _template;

    public Document(Template template)
    {
        ChangeTemplate(template);
    }

    public void ChangeTemplate(Template template)
    {
        _template = template.Copy();
    }
}

The factory method pattern

  • Create specific classes without using them
  • Idea: Tell a central factory which class to create (e.g. by using a string)
  • The factory then knows which class to use and how to create
  • Goal: Maintainability and extendability gain
  • Using DRY: Creation code on a single central place
  • Information hiding is possible if special information is required that only the factory needs to know about

Factory method diagram

skinparam classAttributeIconSize 0
abstract class Product {
}
class Factory {
  +produce(): Product
}
Product <|-- ConcreteProduct1
Product <|-- ConcreteProduct2
Product <|-- ConcreteProductN
Factory -.-> ConcreteProduct1
Factory -.-> ConcreteProduct2
Factory -.-> ConcreteProductN
Consumer --> Product
Consumer -> Factory

Remarks

  • The factory method exists in a lot of variations and makes most sense with otherwise complicated APIs or library extensions
  • Generally one might think of two more methods in the factory to register and unregister new products with instructions on how to create
  • Languages like C# or Java have Reflection built-in, which can enable automatically constructed factories
  • Quite often the factory method pattern is displayed with an abstract class for the factory, but actually this is not required

String based factory

public abstract class SmartPhone
{
     public abstract double Price { get; }

     /* ... */
}
class GalaxyS3 : SmartPhone
{
     public override double Price
     {
         get { return 599.99; }
     }

     /* ... */
}
public class Factory
{
    public SmartPhone Produce(String type)
    {
        switch (type)
        {
            case "GalaxyS3":
                return new GalaxyS3();
            /* ... */
            default:
                return null;
        }
    }
}
class Consumer
{
    private SmartPhone phone;
    private double money;

    public void BuyNewSmartPhone(Factory factory, String type)
    {
         phone = factory.Produce(type);
         money -= phone.Price;
    }
}
public abstract class SmartPhone {
     public abstract double getPrice();

     /* ... */
}
class GalaxyS3 extends SmartPhone {
     @Override 
     public double getPrice() {
         return 599.99;
     }

     /* ... */
}
public class Factory {
    public SmartPhone produce(String type) {
        switch (type) {
            case "GalaxyS3":
                return new GalaxyS3();
            /* ... */
            default:
                return null;
        }
    }
}
class Consumer {
    private SmartPhone phone;
    private double money;

    public void buyNewSmartPhone(Factory factory, String type) {
         phone = factory.produce(type);
         money -= phone.getPrice();
    }
}
class SmartPhone {
     public:
     virtual double getPrice() = 0;

     /* ... */
};
class GalaxyS3 : public SmartPhone {
     public:
     double getPrice() {
         return 599.99;
     }

     /* ... */
};
class Factory {
    public:
    SmartPhone* Produce(string type) {
        if (type == "GalaxyS3")
            return new GalaxyS3();

        return NULL;
    }
};
class Consumer {
    private:
    SmartPhone* phone;
    double money;

    public:
    void BuyNewSmartPhone(Factory* factory, string type)
    {
         phone = factory->Produce(type);
         money -= phone->getPrice();
    }
};

Practical considerations

  • Using the singleton pattern for the factory is often useful
  • If the products follow the prototype pattern then object creation (and registration, if provided) is really simple and straight forward
  • One of the most famous factories can be found in the browser: the DOM only provides access for creating elements over a factory
  • Actually we use already a factory by typing in addresses (scheme)
  • Games quite often make use of factories to produce items
  • The provided abstraction of factories help to reduce dependencies

A factory for commands

interface ICommand
{
    string Info { get; }

    string Help { get; }

    bool CanUndo { get; }

    string[] Calls { get; }

    string FlushOutput();

    bool Invoke(string command, string[] arguments);

    void Undo();
}

class Commands
{
    static Commands instance;

    ICommand[] commands;
    Stack<ICommand> undoList;

    private Commands()
    {
        undoList = new Stack<ICommand>();
        undoCallings = new List<string>();
        commands = Assembly.GetExecutingAssembly()
            .GetTypes()
            .Where(m => !m.IsInterface && typeof(ICommand).IsAssignableFrom(m))
            .Select(m => m.GetConstructor(System.Type.EmptyTypes).Invoke(null) as ICommand)
            .ToArray();
    }

    public static Commands Instance
    {
        get { return instance ?? (instance = new Commands()); }
    }

    public ICommand Create(string command, params string[] args)
    {
        foreach (ICommand cmd in commands)
        {
            if (CheckForCall(cmd, command))
                return cmd;
        }

        return null;
    }

    public bool Invoke(string command, params string[] args)
    {
        ICommand cmd = Create(command, args);

        if (cmd != null && cmd.Invoke(command, args))
        {
            if (cmd.CanUndo)
                undoList.Push(cmd);

            return true;
        }

        return false;
    }
}

Discussion

  • Singletons are sometimes overused, but can be used in many situations
  • The factory method is particularly useful to decouple dependencies on children of classes
  • The ability to clone itself is interesting especially if construction would be impossible otherwise (due to dependencies or language constraints)
  • Decoupling the factory can also be very beneficial, which results in the next pattern: The abstract factory pattern

The abstract factory pattern

  • What if we need specializations of outcoming products?
  • Goal: Provide an interface for creating families of related or dependent objects without specifying their concrete classes
  • Instead of wanting Product instances we want ProductVariant objects, with specialized implementations
  • However, we do not need to know which factory is producing it
  • This decouples a concrete factory from our code and enables application aware productions

Abstract factory diagram

skinparam classAttributeIconSize 0
abstract class Product {
}
abstract class ProductVariant1 {
}
abstract class ProductVariant2 {
}
interface BaseFactory {
  {abstract} +produce1(): ProductVariant1
  {abstract} +produce2(): ProductVariant2
}
class ConcreteFactoryA {
  +produce1(): ProductVariant1
  +produce2(): ProductVariant2
}
class ConcreteFactoryB {
  +produce1(): ProductVariant1
  +produce2(): ProductVariant2
}
ConcreteFactoryA -|> BaseFactory
ConcreteFactoryB -|> BaseFactory
Product <|-- ProductVariant1
Product <|-- ProductVariant2
ProductVariant1 <|-- ConcreteProductA1
ProductVariant2 <|-- ConcreteProductA2
ProductVariant1 <|-- ConcreteProductB1
ProductVariant2 <|-- ConcreteProductB2
ConcreteFactoryA -.-> ConcreteProductA1
ConcreteFactoryA -.-> ConcreteProductA2
ConcreteFactoryB -.-> ConcreteProductB1
ConcreteFactoryB -.-> ConcreteProductB2
Consumer ---> ProductVariant1
Consumer ---> ProductVariant2
Consumer -> BaseFactory

Remarks

  • Abstract factories allow cross-platform development
  • Trick: Extract concrete factory and products into adapter library
  • Exchange adapter library depending on platform
  • There could also be a factory of factories, using both, the Factory method and the abstract factory pattern
  • Abstract factories in this form are not as popular as a plain factory
  • Most popular is a hybrid approach, that takes an abstract factory (e.g. defined in an interface), to produce a single type of product

Method based abstract factory

public abstract class Control
{
    protected abstract void PaintControl();
}
public abstract class Button : Control
{
}
public abstract class TextBox : Control
{
}
class WinButton : Button
{
    protected override void PaintControl()
    {
        /* Implementation */
    }
}
class WinTextBox : TextBox
{
    protected override void PaintControl()
    {
        /* Implementation */
    }
}
class OsxButton : Button
{
    protected override void PaintControl()
    {
        /* Implementation */
    }
}
class OsxTextBox : TextBox
{
    protected override void PaintControl()
    {
        /* Implementation */
    }
}
public interface IFactory
{
    Button CreateButton();
    TextBox CreateTextBox();
}
public class WinFactory : IFactory
{
    public Button CreateButton()
    {
        return new WinButton();
    }

    public TextBox CreateTextBox()
    {
        return new WinTextBox();
    }
}
public class OsxFactory : IFactory
{
    public Button CreateButton()
    {
        return new OsxButton();
    }

    public TextBox CreateTextBox()
    {
        return new OsxTextBox();
    }
}
public abstract class Control {
    protected abstract void paintControl();
}
public abstract class Button extends Control {
}
public abstract class TextBox extends Control {
}
class WinButton extends Button {
    @Override
    protected void paintControl() {
        /* Implementation */
    }
}
class WinTextBox extends TextBox {
    @Override
    protected void paintControl() {
        /* Implementation */
    }
}
class OsxButton extends Button {
    @Override
    protected void paintControl() {
        /* Implementation */
    }
}
class OsxTextBox extends TextBox {
    @Override
    protected void paintControl() {
        /* Implementation */
    }
}
public interface Factory {
    Button createButton();
    TextBox createTextBox();
}
public class WinFactory implements Factory {
    public Button createButton() {
        return new WinButton();
    }

    public TextBox createTextBox() {
        return new WinTextBox();
    }
}
public class OsxFactory implements Factory {
    public Button CreateButton() {
        return new OsxButton();
    }

    public TextBox createTextBox() {
        return new OsxTextBox();
    }
}
class Control {
    protected:
    virtual void PaintControl() = 0;
};
class Button : public Control {
};
class TextBox : public Control {
};
class WinButton : public Button {
    protected:
    void PaintControl() {
        /* Implementation */
    }
};
class WinTextBox : public TextBox {
    protected:
    void PaintControl() {
        /* Implementation */
    }
};
class OsxButton : public Button {
    protected:
    void PaintControl() {
        /* Implementation */
    }
};
class OsxTextBox : public TextBox {
    protected:
    void PaintControl() {
        /* Implementation */
    }
};
class Factory {
    public:
    virtual Button* CreateButton() = 0;
    virtual TextBox* CreateTextBox() = 0;
};
class WinFactory : public Factory {
    public:
    Button* CreateButton() {
        return new WinButton();
    }

    TextBox* CreateTextBox() {
        return new WinTextBox();
    }
};
class OsxFactory : public Factory {
    public:
    Button* CreateButton()
    {
        return new OsxButton();
    }

    TextBox* CreateTextBox() {
        return new OsxTextBox();
    }
};

Practical considerations

  • The abstract factory pattern is quite often used with dependency injection (DI), which will be introduced later
  • Here the concrete products will be resolved automatically
  • Also the factory can then be resolved automatically
  • This yields a maximum an flexibility and minimizes the coupling
  • There are complete frameworks like ADO.NET build upon this
  • Idea: Abstract the interface to the database and hide concrete implementations for specific databases (like MySQL, MariaDB, ...)

ADO.NET abstract factory

skinparam classAttributeIconSize 0
abstract class DbProviderFactory {
  +CreateConnection(): DbConnection
  +CreateCommand(): DbCommand
}
abstract class DbConnection {
}
abstract class DbCommand {
}
SqlClientFactory -|> DbProviderFactory
OracleClientFactory --|> DbProviderFactory
OleDbFactory ---|> DbProviderFactory
OdbcFactory ----> DbProviderFactory
SqlCommand -|> DbCommand
OleDbCommand ---|> DbCommand
OracleCommand --|> DbCommand
OdbcCommand ----|> DbCommand
SqlConnection -|> DbConnection
OracleConnection --|> DbConnection
OleDbConnection ---|> DbConnection
OdbcConnection ----|> DbConnection
SqlClientFactory -.-> SqlConnection
SqlClientFactory -.-> SqlCommand
OracleClientFactory -.-> OracleConnection
OracleClientFactory -.-> OracleCommand
OleDbFactory -.-> OleDbConnection
OleDbFactory -.-> OleDbCommand
OdbcFactory -.-> OdbcConnection
OdbcFactory -.-> OdbcCommand

The builder pattern

  • Quite often a defined sequence of method calls is required for correctly initializing objects
  • This scenario goes far beyond what a constructor should deliver
  • Idea: Create a pattern, which follows the right sequence by design
  • The builder pattern enables the step-by-step creation of complex objects using the correct sequence of actions
  • Here the construction is controlled by a director object that only needs to know the type of object it is to create

Builder diagram

skinparam classAttributeIconSize 0
class Director {
  +Director(Builder)
  +construct()
}
abstract class Builder {
  {abstract} +buildPart()
}
class ConcreteBuilder {
  +buildPart()
  +getResult(): Product
}
class Product {
}
ConcreteBuilder -.-> Product : "«create»"
ConcreteBuilder -|> Builder
Director o-> Builder

Remarks

  • The flow requires that a calling object needs to know the concrete builder and the director
  • In general the directory does not require to get the builder over the constructor, it could also be set by using a property
  • However, the director requires a builder, instance which is reflected by such a design
  • The director handles the whole construction process
  • In the end one just has to call getResult() on the chosen builder

Fluent interface

  • The builder pattern is a solution to the telescoping constructor problem
  • Instead of using numerous constructors, the builder pattern uses another object, that receives each initialization parameter step by step
  • Builders are good candidates for a fluent API using the fluent interface
  • This can build upon chaining, i.e. we always return something useful (if nothing would be returned, then the current instance should be returned)

Simple builder

public interface IBuilder
{
    void BuildPartA();
    void BuildPartB();
    Product Result() { get; }
}
static class Director
{
    public static void Construct(IBuilder builder)
    {
        builder.BuildPartA();
        builder.BuildPartB();
    }
}
class ConcreteBuilder : IBuilder
{
    private Product _product;

    public ConcreteBuilder
    {
        _product = new Product();
    }

    public void BuildPartA()
    {
        /* ... */
    }

    public void BuildPartB()
    {
        /* ... */
    }

    public Product Result
    {
        get { return _product; }
    }
}    
class Product
{
    /* ... */
}
public interface Builder {
    void buildPartA();
    void buildPartB();
    Product getResult();
}
static class Director {
    public static void construct(Builder builder) {
        builder.buildPartA();
        builder.buildPartB();
    }
}
class ConcreteBuilder implements Builder {
    private Product _product;

    public ConcreteBuilder {
        _product = new Product();
    }

    public void buildPartA() {
        /* ... */
    }

    public void buildPartB() {
        /* ... */
    }

    public Product getResult() {
        return _product;
    }
}    
class Product {
    /* ... */
}
class Builder {
    public:
    virtual void BuildPartA() = 0;
    virtual void BuildPartB() = 0;
    virtual Product* GetResult() = 0;
};
class Director {
    public:
    static void Construct(Builder builder) {
        builder.BuildPartA();
        builder.BuildPartB();
    }
};
class ConcreteBuilder : public Builder {
    private:
    Product* _product;

    public ConcreteBuilder {
        _product = new Product();
    }

    public void BuildPartA() {
        /* ... */
    }

    public void BuildPartB() {
        /* ... */
    }

    public Product* GetResult() {
        return _product;
    }
};
class Product {
    /* ... */
};

Practical considerations

  • Quite often a static class is enough, with the builder dependency being shifted to the construction function
  • The director might also create intermediate objects, which can then be implicitly buffered or destroyed
  • Sometimes there is no specific director class, but a director method
  • This could be done to centralize the director and minimize dependencies
  • An example would be the construction of database commands

Database command builder

public abstract class DbCommandBuilder
{
    public abstract DbCommand GetDeleteCommand();
    /* ... */
}
public class SqlCommandBuilder : DbCommandBuilder
{
   private DbCommand BuildDeleteCommand(DataTableMapping mappings, DataRow dataRow)
    {
        DbCommand command = this.InitializeCommand(this.DeleteCommand);
        StringBuilder builder = new StringBuilder();
        int parameterCount = 0;
        builder.Append("DELETE FROM ");
        builder.Append(this.QuotedBaseTableName);
        parameterCount = this.BuildWhereClause(
        mappings, dataRow, builder, command, parameterCount, false);
        command.CommandText = builder.ToString();
        RemoveExtraParameters(command, parameterCount);
        this.DeleteCommand = command;
        return command;
    }

    public SqlCommandBuilder(SqlDataAdapter da)
    {
        /* set some properties */
    }

    public override DbCommand GetDeleteCommand()
    {
        DataTableMapping mappings = ComputeMappings();
        DataRow row = ComputeRow();
        return BuildDeleteCommand(mappings, row);
    }
}

Literature

  • Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1995). Design Patterns: Elements of Reusable Object Oriented Software.
  • Freeman, Eric; Freeman, Elisabeth; Kathy, Sierra; Bert, Bates (2004). Head First Design Patterns.
  • Fowler, Martin; Beck, Kent; Brant, John; Opdyke, William; Roberts (1999). Refactoring: Improving the Design of Existing Code.
  • McConnell, Steve (2004). "Design in Construction". Code Complete.