Design Patterns
ICT2622 - Object-Oriented Analysis · Designing Object-Oriented Systems
Design Patterns
Design patterns are standard solutions to common problems in software design. They provide a template for how to solve a problem that can be used in many different situations. Understanding design patterns can improve your ability to create flexible and reusable software.
What are Design Patterns?
A design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. Design patterns are not finished designs that can be transformed directly into code. They are descriptions or templates for how to solve problems that can be used in various situations.
Remember: Design patterns help improve code readability and reduce complexity.
Types of Design Patterns
Design patterns can be classified into three main categories:
- Creational Patterns: These patterns deal with object creation mechanisms. They aim to create objects in a manner suitable to the situation.
- Structural Patterns: These patterns deal with object composition. They help ensure that if one part of a system changes, the entire system does not need to change.
- Behavioural Patterns: These patterns focus on communication between objects. They help define how objects interact and how responsibilities are distributed among them.
Creational Patterns
Creational patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code. Some common creational patterns include:
Singleton Pattern
The Singleton pattern ensures that a class has only one instance and provides a global point of access to it. This is useful when exactly one object is needed to coordinate actions across the system.
class Singleton {
private:
static Singleton* instance;
Singleton() {}
public:
static Singleton* getInstance() {
if (!instance)
instance = new Singleton();
return instance;
}
};
Singleton* Singleton::instance = nullptr;Watch out: Ensure that the Singleton instance is created only once to avoid multiple instances.
Factory Method Pattern
The Factory Method pattern defines an interface for creating an object but allows subclasses to alter the type of objects that will be created. This pattern promotes loose coupling in the code.
class Product {
public:
virtual void use() = 0;
};
class ConcreteProduct : public Product {
public:
void use() {
// Implementation
}
};
class Creator {
public:
virtual Product* factoryMethod() = 0;
};
class ConcreteCreator : public Creator {
public:
Product* factoryMethod() {
return new ConcreteProduct();
}
};Structural Patterns
Structural patterns focus on how classes and objects are composed to form larger structures. Some common structural patterns include:
Adapter Pattern
The Adapter pattern allows the interface of an existing class to be used as another interface. It acts as a bridge between two incompatible interfaces.
class Target {
public:
virtual void request() = 0;
};
class Adaptee {
public:
void specificRequest() {
// Implementation
}
};
class Adapter : public Target {
private:
Adaptee* adaptee;
public:
Adapter(Adaptee* a) : adaptee(a) {}
void request() {
adaptee->specificRequest();
}
};Decorator Pattern
The Decorator pattern allows behaviour to be added to individual objects, either statically or dynamically, without affecting the behaviour of other objects from the same class.
class Component {
public:
virtual void operation() = 0;
};
class ConcreteComponent : public Component {
public:
void operation() {
// Implementation
}
};
class Decorator : public Component {
protected:
Component* component;
public:
Decorator(Component* c) : component(c) {}
void operation() {
component->operation();
}
};Behavioural Patterns
Behavioural patterns focus on how objects communicate with each other. Some common behavioural patterns include:
Observer Pattern
The Observer pattern defines a one-to-many dependency between objects. When one object changes state, all its dependents are notified and updated automatically.
class Subject {
private:
std::list observers;
public:
void attach(Observer* obs) {
observers.push_back(obs);
}
void notify() {
for (auto obs : observers)
obs->update();
}
};
class Observer {
public:
virtual void update() = 0;
};Strategy Pattern
The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. This pattern lets the algorithm vary independently from clients that use it.
class Strategy {
public:
virtual void execute() = 0;
};
class ConcreteStrategyA : public Strategy {
public:
void execute() {
// Implementation A
}
};
class ConcreteStrategyB : public Strategy {
public:
void execute() {
// Implementation B
}
};
class Context {
private:
Strategy* strategy;
public:
void setStrategy(Strategy* s) {
strategy = s;
}
void executeStrategy() {
strategy->execute();
}
};Tip: Familiarise yourself with the different design patterns and when to use them. This knowledge will help you select the appropriate pattern for your specific needs.
Conclusion
Design patterns are essential tools in software design. They provide solutions to common design problems and help create more maintainable and scalable systems. Understanding and applying design patterns can greatly enhance your software development skills.
Check your understanding
- What is a design pattern?
- Explain the Singleton pattern and provide an example.
- What is the difference between structural and behavioural patterns?
- How does the Observer pattern work?