You are on page 1of 12

Design patterns:

Design patterns in object-oriented programming (OOP) are recurring solutions to common


problems that developers face when designing and implementing software. These patterns
are not specific to a particular programming language or technology but rather provide
general guidelines and templates for solving problems in a flexible and maintainable way.
They help developers create software that is more modular, maintainable, and easier to
understand by promoting good coding practices and encapsulating best practices in design.

Here are some of the most commonly used design patterns in OOP:

Creational Patterns:

Singleton Pattern: Ensures that a class has only one instance and provides a global point of
access to it.

Factory Method Pattern: Defines an interface for creating an object but allows subclasses to
alter the type of objects that will be created.

Abstract Factory Pattern: Provides an interface for creating families of related or dependent
objects without specifying their concrete classes.

Builder Pattern: Separates the construction of a complex object from its representation,
allowing the same construction process to create different representations.

Prototype Pattern: Creates new objects by copying an existing object, known as the
prototype.

Structural Patterns:

Adapter Pattern: Allows the interface of an existing class to be used as another interface.

Decorator Pattern: Attaches additional responsibilities to objects dynamically.

Composite Pattern: Composes objects into tree structures to represent part-whole


hierarchies.

Proxy Pattern: Provides a surrogate or placeholder for another object to control access to it.

Bridge Pattern: Separates an object’s abstraction from its implementation so that the two can
vary independently.
Behavioral Patterns:

Observer Pattern: Defines a one-to-many dependency between objects so that when one
object changes state, all its dependents are notified and updated automatically.

Strategy Pattern: Defines a family of algorithms, encapsulates each one, and makes them
interchangeable.

Command Pattern: Encapsulates a request as an object, thereby allowing for


parameterization of clients with queues, requests, and operations.

State Pattern: Allows an object to alter its behavior when its internal state changes.

Memento Pattern: Captures and externalizes an object's internal state so the object can be
restored to this state later.

Architectural Patterns:

Model-View-Controller (MVC) Pattern: Separates an application into three interconnected


components: the Model (data and logic), the View (presentation), and the Controller (user
interface).

Model-View-ViewModel (MVVM) Pattern: A variation of MVC that separates the user


interface logic from the application's data and behavior.

Repository Pattern: Separates the logic that retrieves data from the underlying data store
from the rest of the application.

Design patterns are not mandatory in software development, but they are valuable tools for
improving code quality, reducing redundancy, and promoting good software design
principles. They offer proven, reusable solutions to common problems and can significantly
enhance the maintainability and extensibility of software systems.

Solid principles

The SOLID principles are a set of five design principles in object-oriented programming
(OOP) that aim to make software more maintainable, flexible, and understandable. These
principles were introduced by Robert C. Martin and represent a set of guidelines for writing
clean, maintainable, and scalable code. The acronym SOLID stands for the following
principles:

Single Responsibility Principle (SRP):


A class should have only one reason to change. In other words, a class should have a
single, well-defined responsibility or job.
This principle encourages the separation of concerns and helps in maintaining and
understanding code by keeping each class focused on a specific task.
Open-Closed Principle (OCP):

Software entities (such as classes, modules, and functions) should be open for extension but
closed for modification.
This principle encourages developers to design their software in a way that allows new
functionality to be added by creating new classes or components without altering existing,
stable code.
Liskov Substitution Principle (LSP):

Subtypes (derived or child classes) must be substitutable for their base types (parent or
superclasses) without affecting the correctness of the program.
In practical terms, this means that any derived class should be able to replace its base class
without causing unexpected or incorrect behavior.
Interface Segregation Principle (ISP):

Clients should not be forced to depend on interfaces they do not use. In other words, it's
better to have smaller, more specific interfaces rather than large, monolithic ones.
This principle encourages the creation of fine-grained interfaces tailored to the needs of the
client classes that implement them.
Dependency Inversion Principle (DIP):

High-level modules (such as abstractions and interfaces) should not depend on low-level
modules (concrete implementations). Both should depend on abstractions.
Abstractions (interfaces or abstract classes) should not depend on details; details should
depend on abstractions.
This principle promotes decoupling between high-level and low-level modules and
encourages the use of interfaces and dependency injection to make the system more flexible
and maintainable.
By adhering to these SOLID principles, software developers can create code that is more
modular, extensible, and easier to maintain. These principles are considered fundamental to
good object-oriented design and are widely used in software development to promote the
creation of robust and adaptable systems.

# Violation of SRP
class Employee:
def calculate_salary(self):
# Calculate the salary
pass

def save_to_database(self):
# Save employee data to the database
pass
# Adhering to SRP
class Employee:
def calculate_salary(self):
# Calculate the salary
pass

class EmployeeDatabase:
def save_to_database(self, employee):
# Save employee data to the database
pass

# Violation of OCP
class Circle:
def draw(self):
# Draw a circle

class Square:
def draw(self):
# Draw a square

# To add a new shape, you need to modify existing code.


# Adhering to OCP
class Shape:
def draw(self):
pass

class Circle(Shape):
def draw(self):
# Draw a circle

class Square(Shape):
def draw(self):
# Draw a square

# You can add new shapes without modifying existing code.

# Violation of LSP
class Bird:
def fly(self):
pass

class Ostrich(Bird):
def fly(self):
# Ostriches can't fly, so this is incorrect
# Adhering to LSP
class Bird:
def move(self):
pass

class Sparrow(Bird):
def move(self):
# Sparrows can fly

class Ostrich(Bird):
def move(self):
# Ostriches can't fly, so they run

# Violation of ISP
class Worker:
def work(self):
pass

def eat(self):
pass

# Adhering to ISP
class Workable:
def work(self):
pass

class Eatable:
def eat(self):
pass

class Worker(Workable, Eatable):


pass

# Violation of DIP
class LightBulb:
def turn_on(self):
pass

class Switch:
def __init__(self, bulb):
self.bulb = bulb

def operate(self):
if self.bulb.is_on():
self.bulb.turn_off()
else:
self.bulb.turn_on()

# Adhering to DIP
class Switchable:
def turn_on(self):
pass

def turn_off(self):
pass

class LightBulb(Switchable):
def is_on(self):
pass

class Switch:
def __init__(self, device):
self.device = device

def operate(self):
if self.device.is_on():
self.device.turn_off()
else:
self.device.turn_on()
Creational patterns

Singleton Pattern:

Ensures a class has only one instance.

Example in Python:

class Singleton:
_instance = None

def __new__(cls):
if cls._instance is None:
cls._instance = super(Singleton, cls).__new__(cls)
return cls._instance

Factory Method Pattern:

Defines an interface for creating objects but lets subclasses decide which class to
instantiate.

Example in Python:
from abc import ABC, abstractmethod

class Creator(ABC):
@abstractmethod
def factory_method(self):
pass

class ConcreteCreatorA(Creator):
def factory_method(self):
return ConcreteProductA()

class ConcreteCreatorB(Creator):
def factory_method(self):
return ConcreteProductB()

Abstract Factory Pattern:

Provides an interface for creating families of related or dependent objects without specifying
their concrete classes.

Example in Python:

from abc import ABC, abstractmethod

class AbstractFactory(ABC):
@abstractmethod
def create_product_a(self):
pass

@abstractmethod
def create_product_b(self):
pass

class ConcreteFactory1(AbstractFactory):
def create_product_a(self):
return ProductA1()

def create_product_b(self):
return ProductB1()

Builder Pattern:

Separates the construction of a complex object from its representation.

Example in Python:
class Director:
def __init__(self, builder):
self.builder = builder

def construct(self):
self.builder.build_part_a()
self.builder.build_part_b()

class Builder:
def build_part_a(self):
pass

def build_part_b(self):
pass

Prototype Pattern:

Creates new objects by copying an existing object.

Example in Python:

import copy

class Prototype:
def clone(self):
return copy.copy(self)

Structural Patterns:

Adapter Pattern:

Allows the interface of an existing class to be used as another interface.

Example in Python:

class Target:
def request(self):
pass

class Adaptee:
def specific_request(self):
pass

class Adapter(Target):
def __init__(self, adaptee):
self.adaptee = adaptee

def request(self):
self.adaptee.specific_request()

Decorator Pattern:

Attaches additional responsibilities to objects dynamically.

Example in Python:

class Component:
def operation(self):
pass

class ConcreteComponent(Component):
def operation(self):
pass

class Decorator(Component):
def __init__(self, component):
self._component = component

def operation(self):
self._component.operation()

Composite Pattern:

Composes objects into tree structures to represent part-whole hierarchies.

Example in Python:

class Component:
def operation(self):
pass
class Leaf(Component):
def operation(self):
pass

class Composite(Component):
def operation(self):
for component in self._children:
component.operation()

Proxy Pattern:

Provides a surrogate or placeholder for another object to control access to it.

Example in Python:

class Subject:
def request(self):
pass

class RealSubject(Subject):
def request(self):
pass

class Proxy(Subject):
def __init__(self, real_subject):
self._real_subject = real_subject

def request(self):
self._real_subject.request()

Bridge Pattern:

Separates an object's abstraction from its implementation.

Example in Python:

class Abstraction:
def __init__(self, implementation):
self._implementation = implementation

def operation(self):
self._implementation.operation_implementation()
class Implementor:
def operation_implementation(self):
pass

class ConcreteImplementorA(Implementor):
def operation_implementation(self):
pass

You might also like