Software Design Patterns

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

Software Design Patterns

Introduction to modern software architecture

software architecture

Structural patterns

Introduction

  • Deal with the structure of a program
  • Try to ease the design by identifying ways to realize certain relationships
  • They are concerned with how classes and objects are composed to form larger structures
  • Goal: Clean big picture, with a better (reliable, extensible, ...) layout
  • Structural class patterns use inheritance to compose interfaces or implementations

The adapter pattern

  • Old problem: One class wants to talk to another class by using a specific method, which is named differently
  • Solution in the real world: An adapter is required!
  • The adapter pattern tries to allow communication between two incompatible types
  • The central class is called the Adapter
  • This class knows about the Adaptee and the specific Target that is required from a client

Adapter diagram

skinparam classAttributeIconSize 0
interface Target {
  {abstract} +methodA()
}
class Adaptee {
  +methodB()
}
class Adapter {
  -adaptee: Adaptee
  +methodA()
}
Client -.-> Target
Adapter -.-|> Target
Adapter "1" -> "1" Adaptee

Remarks

  • The adapter pattern is always required when we want to enable communication between two boxed systems
  • Sometimes this is also known as Wrapper
  • It can create a reusable class that cooperates with unrelated classes that have incompatible interfaces
  • One-way, two-way? Classically only a one-way solution is supported
  • Two-way requires interfaces (Java, C#) or multiple-inheritance (C++)

A simple adapter

public interface ITarget
{
    void MethodA();
}
public class Client
{
    private readonly ITarget _target;

    public Client(ITarget target)
    {
        _target = target;
    }

    public void Request()
    {
        _target.MethodA();
    }
}
public class Adaptee
{
    public void MethodB()
    {
        /* ... */
    }
}
public class Adapter : ITarget
{
    private readonly Adaptee _adaptee = new Adaptee();

    public void MethodA()
    {
        _adaptee.MethodB();
    }
}
public interface Target {
    void methodA();
}
public class Client {
    private final Target _target;

    public Client(Target target) {
        _target = target;
    }

    public void Request() {
        _target.methodA();
    }
}
public class Adaptee {
    public void methodB() {
        /* ... */
    }
}
public class Adapter implements Target {
    private final Adaptee _adaptee = new Adaptee();

    public void methodA() {
        _adaptee.MethodB();
    }
}
class Target {
    public:
    virtual void MethodA() = 0;
};
class Client {
    private:
    Target* _target;

    public:
    Client(Target* target) {
        _target = target;
    }

    void Request() {
        _target->MethodA();
    }
};
class Adaptee {
    public:
    void MethodB() {
        /* ... */
    }
};
class Adapter : public Target {
    private:
    Adaptee* _adaptee;

    public:
    void Adapter() {
        _adaptee = new Adaptee();
    }

    public:
    void MethodA() {
        _adaptee->MethodB();
    }
};

Practical considerations

  • If the class inheriting from one class has a reference to the other class we call it an aggregate
  • Otherwise if we do only method renaming for generating compatibility we call it a compatible
  • In practice the adapter pattern is also used within the same library
  • Some of the upcoming patterns are also helpful for generating a common communication platform

Do you see the adapter?

public class ButtonDemo {
	public ButtonDemo() {
		Button button = new Button("Press me");
		button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				doOperation();
			}
		});
	}
	public void doOperation() { 
		/* ... */
	}
}

The proxy pattern

  • A proxy is an object that performs transparent communication by tunneling through a system
  • Usually we are using proxies to hide the real data / implementation
  • A proxy is a class with methods, which has a reference to another class with data
  • Only the proxy can modify the data or execute methods of the data class
  • This has security advantages, however, only at compiler level (not RT)

Proxy diagram

skinparam classAttributeIconSize 0
abstract class SubjectBase {
  +{abstract} operation()
}
class ConcreteSubject extends SubjectBase {
  +operation()
}
class Proxy extends SubjectBase {
  -subject: ConcreteSubject
  +operation()
}
Proxy "1" -> "1" ConcreteSubject

Remarks

  • Proxies enable indirect access to data
  • We have security and memory advantages
  • The data has handled only by one class
  • The data is encapsulated and passed by reference
  • The abstraction is usually contained in an interface
  • Implementation-wise an abstract class could be better (nested classes can access private members of parent class objects)

A transparent wrapper

public abstract class SubjectBase
{
    public abstract void Operation();
}
public class ConcreteSubject : SubjectBase
{
    public override void Operation()
    {
        /* ... */
    }
}
public class Proxy : SubjectBase
{
    private ConcreteSubject subject;

    public override void Operation()
    {
        if (subject == null)
            subject = new ConcreteSubject();

        subject.Operation();
    }
}
public abstract class SubjectBase {
    public abstract void operation();
}
public class ConcreteSubject extends SubjectBase {
    @Override
    public void operation() {
        /* ... */
    }
}
public class Proxy extends SubjectBase {
    private ConcreteSubject subject;

    @Override
    public void operation() {
        if (subject == null)
            subject = new ConcreteSubject();

        subject.operation();
    }
}
class SubjectBase {
    public:
    virtual void Operation() = 0;
};
class ConcreteSubject : public SubjectBase {
    public:
    void Operation() {
        /* ... */
    }
};
class Proxy : public SubjectBase {
    private:
    ConcreteSubject* subject;

    public:
    void Operation() {
        if (subject == NULL)
            subject = new ConcreteSubject();

        subject->Operation();
    }
};

Practical considerations

  • Usually the proxy pattern is a way to minimize data duplication
  • This is then a special case of the flyweight pattern (upcoming)
  • Sometimes proxies come with a static instance counter
  • Multiple proxies increase the instance counter and use the same data object
  • Once no instances are left, the data object is disposed
  • The reference counter pointer object is an implementation of this pattern

The bridge pattern

  • A bridge allows us to reach the other side of a river or valley
  • Here we connect to an object of a certain type
  • The specific object could be changed
  • Therefore we separate the abstraction from the implementation
  • Changes in the implementation do not affect the abstraction
  • This is very helpful as seen with the state pattern

Bridge diagram

skinparam classAttributeIconSize 0
abstract class Implementor {
  +{abstract} operationImplementation()
}
abstract class Abstraction {
  +implementator: Implementator
  +{abstract} operation()
}
class RefinedAbstraction extends Abstraction {
  +operation()
}
class ConcreteImplementor extends Implementor {
  + operationImplementation()
}
Abstraction "1" -> "1" Implementor

Remarks

  • The bridge pattern provides a cleaner implementation of real-world objects
  • The implementation details can be changed easily
  • Additionally it is possible to consider a variation of implementations
  • Also the bridge pattern decouples the usage of a concrete implementation with a client, which ensures testability and robustness

A simple abstraction layer

abstract class Implementor
{
    public abstract void Execute();
}
class Abstraction
{
    protected Implementor implementor;

    public Implementor Implementor
    {
        get { return implementor; }
        set { implementor = value; }
    }

    public virtual void Operation()
    {
        implementor.Execute();
    }
}
class RefinedAbstraction : Abstraction
{
    public override void Operation()
    {
        implementor.Execute();
    }
}
class ConcreteImplementor : Implementor
{
    public override void Execute()
    {
        /* ... */
    }
}
abstract class Implementor {
    public abstract void execute();
}
class Abstraction {
    protected Implementor implementor;

    public void setImplementor(Implementor value) {
        implementor = value;
    }

    public Implementor getImplementor() {
        return implementor;
    }

    public virtual void operation() {
        implementor.execute();
    }
}
class RefinedAbstraction extends Abstraction {
    @Override
    public void operation() {
        implementor.execute();
    }
}
class ConcreteImplementor extends Implementor {
    @Override
    public void execute() {
        /* ... */
    }
}
class Implementor {
    public:
    virtual void Execute() = 0;
};
class Abstraction {
    protected:
    Implementor* implementor;

    public:
    void SetImplementor(Implementor* value) {
        implementor = value;
    }

    Implementor* GetImplementor() {
        return implementor;
    }

    virtual void Operation() {
        implementor->Execute();
    }
};
class RefinedAbstraction : public Abstraction {
    public:
    void Operation() {
        implementor->Execute();
    }
};
class ConcreteImplementor : public Implementor {
    public:
    void Execute() {
        /* ... */
    }
};

Practical considerations

  • Practically the bridge pattern is used quite often with interfaces
  • Interfaces already bring in some advantages like testability (mocking)
  • If the implementation is defined by an interface, then the bridge does not contain any unnecessary overhead
  • The proxy might also be responsible for handling the resources
  • This pattern is useful to share an implementation among multiple objects

An implementation changer

public abstract class MessageSenderBase {
    public abstract void sendMessage(string title, string details, int importance);
}
public class WebServiceSender : MessageSenderBase {
    @Override
    public void sendMessage(string title, string body, int importance) {
        out.println("Web Service\n" + title + "\n" + body + "\n" + importance);
    }
}
public class EmailSender : MessageSenderBase {
    @Override
    public void sendMessage(string title, string body, int importance) {
        out.println("Email\n" + title + "\n" + body + "\n" + importance);
    }
}
public class MessageQueueSender : MessageSenderBase {
    @Override
    public void sendMessage(string title, string body, int importance) {
        out.println("Message Queue\n" + title + "\n" + body + "\n" + importance);
    }
}
public class Message {
    protected string title;
    protected string body;
    protected int importance;
    protected MessageSenderBase messageSender;

    public Message() {
        this.messageSender = new EmailSender();
    }

    public string getTitle() {
        return title;
    } 
    public void setTitle(string value) {
        title = value;
    }
    public string getImportance() {
        return importance;
    } 
    public void setImportance(string value) {
        importance = value;
    }
    public string getBody() {
        return body;
    } 
    public void setBody(string value) {
        body = value;
    }
    public virtual void send() {
        messageSender.sendMessage(title, body, importance);
    }
}
public class UserEditedMessage : Message {
    string userComments;

    public UserEditedMessage(MessageSenderBase messageSender) {
        this.messageSender = messageSender;
    }
    
    public string getUserComments() {
        return userComments;
    } 
    public void setUserComments(string value) {
        userComments = value;
    }
    @Override
    public void send() {
        messageSender.sendMessage(title, body + "\n" + userComments, importance);
    }
}

The facade pattern

  • A facade is a nice wall that hides the underlying building
  • Usually a facade is used to provide the API behind a complex system
  • Also it might be useful to bundle dependencies on external libraries
  • This reduces dependencies for other packages
  • A facade can create a new API that calls the required methods of the other APIs
  • Hence a facade bundles different classes to achieve a common goal

Facade diagram

skinparam classAttributeIconSize 0
class Facade {
  +Operation()
}
package Lib1 <<Folder>> {
class ClassA
class ClassB
}
package Lib2 <<Folder>> {
class ClassC
class ClassD
}
Facade ..> Lib1
Facade ..> Lib2

Remarks

  • Classically we have one top class that contains references to each class's instance
  • Each class represents a set of subtasks
  • The facade's methods use several methods of the contained instances
  • This constructs a save way of communicating to instances that do not share a common basis, but a common goal
  • Testing an API by only testing the top-level increases testability
  • The facade pattern also enhances readability

Wrapping libraries

public class Facade
{
    public void PerformAction()
    {
        var c1a = new Class1A();
        var c1b = new Class1B();
        var c2a = new Class2A();
        var c2b = new Class2B();
        var result1a = c1a.Func();
        var result1b = c1b.Func(result1a);
        var result2a = c2a.Func(result1a);
        c2b.Action(result1b, result2a);
    }
}
public class Class1A
{
    public int Func()
    {
        Console.WriteLine("Class1A.Func return value: 1");
        return 1;
    }
}
public class Class1B
{
    public int Func(int param)
    {
        Console.WriteLine("Class1B.Func return value: {0}", param + 1);
        return param+1;
    }
}
public class Class2A
{
    public int Func(int param)
    {
        Console.WriteLine("Class2A.Func return value: {0}", param + 2);
        return param+2;
    }
}
public class Class2B
{
    public void Action(int param1, int param2)
    {
        Console.WriteLine("Class2B.Action received: {0}", param1 + param2);
    }
}
public class Facade {
    public void performAction() {
        Class1A c1a = new Class1A();
        Class1B c1b = new Class1B();
        Class2A c2a = new Class2A();
        Class2B c2b = new Class2B();
        int result1a = c1a.func();
        int result1b = c1b.func(result1a);
        int result2a = c2a.func(result1a);
        c2b.action(result1b, result2a);
    }
}
public class Class1A {
    public int func() {
        out.println("Class1A.func return value: 1");
        return 1;
    }
}
public class Class1B {
    public int func(int param) {
        out.println("Class1B.func return value: " + param + 1);
        return param + 1;
    }
}
public class Class2A {
    public int func(int param) {
        out.println("Class2A.func return value: " + param + 2);
        return param + 2;
    }
}
public class Class2B {
    public void action(int param1, int param2) {
        out.println("Class2B.action received: " + param1 + param2);
    }
}
class Facade {
    public void PerformAction() {
        Class1A c1a;
        Class1B c1b;
        Class2A c2a;
        Class2B c2b;
        int result1a = c1a.Func();
        int result1b = c1b.Func(result1a);
        int result2a = c2a.Func(result1a);
        c2b.Action(result1b, result2a);
    }
};
class Class1A {
    public:
    int Func() {
        cout << "Class1A.func return value: 1";
        return 1;
    }
};
class Class1B {
    public:
    int Func(int param) {
        cout << "Class1B.func return value: " << param << 1;
        return param + 1;
    }
};
class Class2A {
    public:
    int Func(int param) {
        cout << "Class2A.func return value: " << param << 2;
        return param + 2;
    }
};
class Class2B {
    public:
    void Action(int param1, int param2) {
        cout << "Class2B.action received: " << param1 << param2;
    }
};

Practical considerations

  • Quite often poorly designed APIs are also wrapped with a facade
  • A facade is used when one wants an easier or simpler interface to an underlying implementation object
  • The difference to an adapter is that an adapter also respects a particular interface and supports polymorphic behavior
  • A facade does not support polymorphic behavior
  • Here a decorator (upcoming) should be used for extensions

The flyweight pattern

  • Several objects containing unique data and shared data
  • A static class containing the shared data objects (or Singleton)
  • A class to represent the shared data
  • This should reduce the memory footprint
  • Reducing the memory requirement will increase the performance
  • In certain cases this can improve consistency

Flyweight diagram

skinparam classAttributeIconSize 0
class FlyweightFactory {
  -flyweights: FlyweightBase [0..n]
}
abstract class FlyweightBase {
  -state
  +operation(target)
}
class ConcreteFlyweight extends FlyweightBase {
}
FlyweightFactory "1" o-> "1" FlyweightBase

Remarks

  • This pattern makes most sense when a huge amount of data is considered or a collection of immutable objects
  • The objects should either not change at all or be shared among several clients
  • An application is the string management in languages like Java or C#
  • Here every (unique) string is stored in a string table
  • Most strings therefore do not need to be instantiated, as they are available in the table (otherwise they are created and added)

Flyweight factory

public class FlyweightFactory
{
    private readonly Dictionary<string, FlyweightBase> _flyweights;

    public FlyweightFactory()
    {
        _flyweights = new Dictionary<string, FlyweightBase>();
    }

    public FlyweightBase GetFlyweight(string key)
    {
        if (_flyweights.ContainsKey(key))
        {
            return _flyweights[key];
        }
        else
        {
            var newFlyweight = new ConcreteFlyweight();
            _flyweights.Add(key, newFlyweight);
            return newFlyweight;
        }
    }
}
public class FlyweightFactory {
    private final Map<string, FlyweightBase> _flyweights;

    public FlyweightFactory() {
        _flyweights = new HashMap<string, FlyweightBase>();
    }

    public FlyweightBase getFlyweight(string key) {
        if (_flyweights.containsKey(key))
        {
            return _flyweights.get(key);
        }
        else
        {
            FlyweightBase newFlyweight = new ConcreteFlyweight();
            _flyweights.put(key, newFlyweight);
            return newFlyweight;
        }
    }
}
class FlyweightFactory {
    private:
    map<string, FlyweightBase*> _flyweights;

    public:
    FlyweightBase* GetFlyweight(string key) {
        if (_flyweights.find(key) == _flyweights.end())
        {
            FlyweightBase* newFlyweight = new ConcreteFlyweight();
            _flyweights[key] = newFlyweight;
        }

        return _flyweights[key];
    }
};

Practical considerations

  • The factory might follow the factory pattern
  • However, instead of instantiating objects we just return them
  • In some cases (lazy loading) we might return special objects ...
  • ... or instantiate objects if certain criteria are met
  • Sometimes it is useful to include resource management
  • A perfect example is a StringBuilder pool
  • Here we can gain a lot of performance for creating (long) strings on multiple occasions

Sharing states

public Glyph
{
    private int width;
    private int height;
    private int ascent;
    private int descent;
    private int pointSize;

    public int Width
    {
    	get { return width; }
    }

    public int Height
    {
    	get { return height; }
    }

    public int Ascent
    {
    	get { return ascent; }
    }

    public int Descent
    {
    	get { return descent; }
    }

    public int PointSize
    {
    	get { return pointSize; }
    }
}
public Character
{
	char letter;
	int position;
	Glyph properties;

	public Character(char letter, int position)
	{
		this.letter = letter;
		this.position = position;
		this.properties = GlyphFactory.Get(letter);
	}

	public char Letter
	{
		get { return letter; }
	}

	public int Position
	{
		get { return position; }
	}

	public Glyph Properties
	{
		get { return properties; }
	}
}

The decorator pattern

  • Quite often we have to extend existing classes
  • Sometimes however, these classes are sealed or cannot be extended directly
  • A pattern that is helpful in such cases is the decorator pattern
  • Here we need a decorator class that implements the same interface as the abstraction
  • This decorator then requires an object that implements the interface as well

Decorator diagram

skinparam classAttributeIconSize 0

interface Component {
  +operation()
}
class ConcreteComponent implements Component {
  +operation()
}
abstract class Decorator implements Component {
  -component: Component
  +Decorator(Component)
  +operation()
}
class ConcreteDecorator extends Decorator {
  +operation()
  +newOperation()
}
Decorator "1" o-> "1" Component

Remarks

  • There might be classes inheriting from the decorator
  • This concept enables more sophisticated ways of using the interface
  • Actually this pattern is quite close to the builder pattern
  • However, the decorator is more focused on the model itself than on modifying it / general behavior
  • It decouples complexity by using inheritance to specialize objects

Decorating existing classes

public interface IComponent
{
    void Operation();
}
public class ConcreteComponent : IComponent
{
    public void Operation()
    {
        /* ... */
    }
}
public class Decorator : IComponent
{
    private readonly IComponent _component;

    public Decorator(IComponent component)
    {
        _component = component;
    }

    public void Operation()
    {
        _component.Operation();
    }
}
public interface Component {
    void operation();
}
public class ConcreteComponent implements Component {
    public void operation() {
        /* ... */
    }
}
public class Decorator implements Component {
    private final Component _component;

    public Decorator(Component component) {
        _component = component;
    }

    public void operation() {
        _component.operation();
    }
}
class Component {
    public:
    virtual void Operation() = 0;
};
class ConcreteComponent : public Component {
    public:
    void Operation() {
        /* ... */
    }
};
class Decorator : public Component {
    private:
    const Component* _component;

    public:
    Decorator(Component* component) : _component(component) {
    }

    void Operation() {
        _component->Operation();
    }
}

Practical considerations

  • The decorater pattern works best if the component is an interface
  • In principle this is also a wrapper, however, a non-transparent one
  • This wrapper might also extend the existing interface, by e.g. adding new operations or properties
  • The big advantage is that this wrapper is pluggable, i.e. it can be used with any object of the given time at runtime
  • A decorator could also be used as input for a decorator (stacking)

Sandwiches

public abstract class Sandwich
{
    private string description;

    public abstract double Price { get; }

    public virtual string Description
    {
        get { return description; }
        protected set { description = value; }
    }
}
public class TunaSandwich : Sandwich
{
    public TunaSandwich()
    {
        Description = "Tuna Sandwich";
    }

    public override double Price
    {
        get { return 4.10; }
    }
}
public class SandwichDecorator : Sandwich
{
    protected Sandwich sandwich;
    private string description;

    public SandwichDecorator(Sandwich sandwich)
    {
        this.sandwich = sandwich;
    }

    public override string Description
    {
        get { return sandwich.Description + ", " + description; }
        protected set { description = value; }
    }
    public override double Price
    {
        get { return sandwich.Price; }
    }
}
public class Cheese : SandwichDecorator
{
    public Cheese(Sandwich sandwich) : base(sandwich)
    {
        Description = "Cheese";
    }
    public override double Price
    {
        get { return sandwich.Price + 1.23; }
    }
}
class Sandwich {
    private:
    string description;

    public:
    virtual double GetPrice() = 0;

    virtual string GetDescription() {
        return description;
    }

    virtual void SetDescription(string value) {
        description = value;
    }
};
class TunaSandwich : public Sandwich {
    public:
    TunaSandwich() {
        SetDescription("Tuna Sandwich");
    }

    double GetPrice() {
        return 4.10;
    }
};
class SandwichDecorator : public Sandwich {
    protected:
    Sandwich* _sandwich;

    private:
    string description;

    public:
    SandwichDecorator(Sandwich* sandwich) : _sandwich(sandwich) {
    }
    
    string GetDescription() {
        return _sandwich->GetDescription() + ", " + description;
    }

    void SetDescription(string value) {
        description = value;
    }

    virtual double GetPrice() {
        return _sandwich->GetPrice();
    }
};
class Cheese : public SandwichDecorator {
    public:
    Cheese(Sandwich* sandwich) : SandwichDecorator(sandwich) {
        SetDescription("Cheese");
    }
    double GetPrice() {
        return _sandwich->GetPrice() + 1.23;
    }
};

The composite pattern

  • How to efficiently create tree-like structures?
  • The answer is to follow the composite pattern
  • One object containing several other objects
  • We have two different kind of objects in there, leafs (certainly without child nodes) and composites (possibly containing child nodes)
  • However, both types are subtypes of a Component class
  • Most UI systems follow the composite pattern

Composite diagram

skinparam classAttributeIconSize 0

abstract class Component {
  +{abstract} operation()
}
class Composite extends Component {
  -children: Component [0..n]
  +addChild(Component)
  +getChild(Integer index)
  +operation()
  +removeChild(Component)
}
class Leaf extends Component {
  +operation()
}
Client ..> Component
Composite "1" -> "*" Component

Remarks

  • Ideally the component defines the methods that are shared among all nodes
  • Usually it makes sense to have this component defined as an interface
  • The interface should have a method to allow enumeration
  • The enumeration lists all children and their children
  • This results in a recursive call at each node level
  • Less redundancy and formal more correct than other approaches

Building a tree structure

public abstract class Component
{
    protected readonly string name;

    public Component(string name)
    {
        this.name = name;
    }

    public abstract void Operation();
    public abstract void Show();
}
class Composite : Component
{
    private readonly List<Component> _children;

    public Composite(string name)
        : base(name)
    {
        _children = new List<Component>();
    }

    public void AddChild(Component component)
    {
        _children.Add(component);
    }
    public void RemoveChild(Component component)
    {
        _children.Remove(component);
    }
    public Component GetChild(int index)
    {
        return _children[index];
    }
    public override void Operation()
    {
        Console.WriteLine("Composite with " + _children.Count + " child(ren).");
    }
    public override void Show()
    {
        Console.WriteLine(name);

        foreach (Component component in _children)
        {
            component.Show();
        }
    }
}
public class Leaf : Component
{
    public Leaf(string name)
        : base(name)
    {
    }

    public override void Operation()
    {
        Console.WriteLine("Leaf.");
    }
    public override void Show()
    {
        Console.WriteLine(name);
    }
}
public abstract class Component {
    protected final string name;

    public Component(string name) {
        this.name = name;
    }

    public abstract void operation();
    public abstract void show();
}
class Composite extends Component {
    private final List<Component> _children;

    public Composite(string name) {
        super(name);
        _children = new ArrayList<Component>();
    }

    public void addChild(Component component) {
        _children.add(component);
    }
    public void removeChild(Component component) {
        _children.remove(component);
    }
    public Component getChild(int index) {
        return _children.get(index);
    }
    @Override
    public void operation() {
        out.println("Composite with " + _children.size() + " child(ren).");
    }
    @Override
    public void show() {
        out.println(name);

        for (Component component : _children) {
            component.show();
        }
    }
}
public class Leaf extends Component {
    public Leaf(string name) {
        super(name);
    }

    @Override
    public void operation() {
        out.println("Leaf.");
    }
    @Override
    public void show() {
        out.println(name);
    }
}
class Component {
    protected:
    const string name;

    public:
    Component(string _name) : name(_name) {
    }

    virtual void Operation() = 0;
    virtual void Show() = 0;
};
class Composite : public Component {
    private:
    list<Component*> _children;

    public:
    Composite(string name) : Component(name) {
    }

    void AddChild(Component* component) {
        _children.push_back(component);
    }
    void RemoveChild(Component* component) {
        _children.remove(component);
    }
    Component* GetChild(int index) {
        int i = 0;

        for (list<Component*>::iterator it = _children.begin(); it != _children.end(); ++it) {
            if (i++ == index) {
                return (*it);
            }
        }

        return NULL;
    }
    void Operation() {
        cout << "Composite with " << _children.size() << " child(ren).";
    }
    void Show() {
        cout << name;

        for (list<Component*>::iterator it = _children.begin(); it != _children.end(); ++it) {
            (*it)->Show();
        }
    }
};
class Leaf : public Component {
    public:
    Leaf(string name) : Component(name) {
    }

    void Operation() {
        cout << "Leaf.";
    }
    void Show() {
        cout << name;
    }
};

Snapshot

object "Composite" as root
object "Leaf" as A
object "Leaf" as B
object "Composite" as C
object "Leaf" as D
object "Leaf" as E
object "Leaf" as F
object "Leaf" as G
root --> A
root --> B
root --> C
root --> D
C --> E
C --> F
C --> G

The aggregate pattern

  • Quite similar to the composite pattern is the aggregate pattern
  • This pattern has emerged from Domain-Driven Design (DDD)
  • Instead of treating objects as individuals we consider a single item
  • One object is therefore called the aggregate root, which handles all calls
  • The root ensures the integrity of the aggregate as a whole
  • These aggregates should not be confused with collections
  • Collections are generic, aggregates are specialized and might contain additional fields or multiple collections

Differences

  • The aggregate pattern is based on composition
  • The composite pattern is based on aggregation
  • That being said, it should be obvious that the constructor of an aggregate requires the root object, since its existence is bound to a root
  • An aggregate consists of 1..n objects
  • A composite consists of 0..n objects
  • The aggregate is destroyed when the root object is disposed

Practical considerations

  • An explicit parent reference simplifies moving up the tree
  • It makes sense to define the parent inside the component class
  • Components might be shared unless more than one parent is possible
  • If the number of children is small it can make sense to put the list with children inside the component class
  • Caching might be useful if searching for children is expensive
  • The composite class might contain any data structure from linked lists to trees, arrays and hash tables

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.
  • Hannemann, Jan (2002). Design pattern implementation in Java and AspectJ.
  • Fowler, Martin (2006). Writing Software Patterns.
  • Liskov, Barbara; Guttag, John (2000). Program Development in Java: Abstraction, Specification, and Object-Oriented Design.