0% found this document useful (0 votes)
6 views47 pages

Design Patterns: Creational Patterns Overview

This lecture covers design patterns, focusing on creational patterns such as Factory Method, Abstract Factory, Prototype, Builder, and Singleton. Design patterns are solutions to recurring design problems that enhance reusability and maintainability in software development. Each pattern is explained with its intent, structure, and examples in Python, illustrating how they can be implemented effectively.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views47 pages

Design Patterns: Creational Patterns Overview

This lecture covers design patterns, focusing on creational patterns such as Factory Method, Abstract Factory, Prototype, Builder, and Singleton. Design patterns are solutions to recurring design problems that enhance reusability and maintainability in software development. Each pattern is explained with its intent, structure, and examples in Python, illustrating how they can be implemented effectively.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

EECE 4520 – Lecture 11

Design Patterns Part I

Roi Yehoshua
Agenda
 Design patterns
 Creational patterns
 Factory method
 Abstract factory
 Prototype
 Builder
 Singleton

2 Roi Yehoshua, 2025


What are Design Patterns?
 Design patterns are recurring solutions to design problems you see over and over
 Promote reusability, extensibility and ease of maintenance
 Patterns provide a shared language among developers
 Patterns don’t give you code, they give you experience
 Each design pattern is described by:
 Intent of the pattern briefly describes both the problem and the solution
 Motivation further explains the problem and the solution the pattern makes possible
 Structure (UML) of classes shows each part of the pattern and how they are related
 Code example in a programming language makes it easier to grasp the pattern

3 Roi Yehoshua, 2025


GoF Design Patterns
 The 23 Gang of Four (GoF) patterns are considered the foundational design patterns
 Divided into 3 groups:
 Creational patterns: deal with object creation
 Structural patterns: how classes are composed to form large structures
 Behavioral patterns: deal with behaviors of objects

4 Roi Yehoshua, 2025


General Principles
 Identify the parts of the application that vary and separate them from those that stay
the same. Encapsulate what varies.
 Program to an interface, not an implementation
 Favor composition over inheritance
 Strive for loosely coupled designs between objects

5 Roi Yehoshua, 2025


Creational Design Patterns
 Creational patterns provide various object creation mechanisms, which increase
flexibility and reuse of existing code

6 Roi Yehoshua, 2025


Factory Method
 Provide an interface for creating objects, without exposing the creation logic to the
client
 Lets subclasses decide which type of object to instantiate

Creator
Product

+FactoryMethod() product = FactoryMethod()


+AnOperation()

ConcreteProduct ConcreteCreator

+FactoryMethod() return new ConcreteProduct

7 Roi Yehoshua, 2025


Factory Method Example
 Assume that we have a hierarchy of shapes that implement the Shape interface
 We would like to allow the user to choose the type of shape to create
 To that end, we create a factory class called ShapeFactory that creates a Shape object
based on the information passed to it

8 Roi Yehoshua, 2025


Example: Product Interface
# [Link]
from abc import ABC, abstractmethod

class Shape(ABC):
@abstractmethod
def draw(self):
pass

9 Roi Yehoshua, 2025


Example: Concrete Products
# [Link]
from shape import Shape

class Circle(Shape):
def draw(self):
print('Drawing a circle')

# [Link]
from shape import Shape

class Rectangle(Shape):
def draw(self):
print('Drawing a rectangle')

# [Link]
from shape import Shape

class Square(Shape):
def draw(self):
print('Drawing a square')

10 Roi Yehoshua, 2025


Example: Factory Class
# shape_factory.py
from shape import Shape
from circle import Circle
from square import Square
from rectangle import Rectangle

class ShapeFactory:
def get_shape(self, shape_type: str) -> Shape:
if shape_type == 'circle':
return Circle()
elif shape_type == 'square':
return Square()
elif shape_type == 'rectangle':
return Rectangle()
raise ValueError('Unknown shape type')

11 Roi Yehoshua, 2025


Example: Main Script
# [Link]
from shape_factory import ShapeFactory Drawing a circle
Drawing a square
factory = ShapeFactory() Drawing a rectangle
shape1 = factory.get_shape('circle')
[Link]()

shape2 = factory.get_shape('square')
[Link]()

shape3 = factory.get_shape('rectangle')
[Link]()

12 Roi Yehoshua, 2025


Factory Method
 Pros:
 Single Responsibility Principle: you can move the products construction code into one place
 Open/Closed Principle: you can introduce new types of products into the program without
breaking existing client code
 Cons:
 Code may become more complicated

13 Roi Yehoshua, 2025


Abstract Factory
 Lets you produce families of related objects without specifying their concrete classes
AbstractFactory
Client

+CreateProductA()
+CreateProductB()

AbstractProductA

ConcreteFactory1 ConcreteFactory2

+CreateProductA() +CreateProductA()
+CreateProductB() +CreateProductB()
ProductA1 ProductA2

AbstractProductB

ProductB1 ProductB2

14 Roi Yehoshua, 2025


Abstract Factory Example

15 Roi Yehoshua, 2025


Example: UI Elements Interface
# [Link]
from abc import ABC, abstractmethod

class Button(ABC):
@abstractmethod
def draw(self):
pass

# [Link]
from abc import ABC, abstractmethod

class Checkbox(ABC):
@abstractmethod
def draw(self):
pass

16 Roi Yehoshua, 2025


Example: Windows Concrete Elements
# win_button.py
from button import Button

class WinButton(Button):
def draw(self):
print('Drawing a Windows button')

# win_checkbox.py
from checkbox import Checkbox

class WinCheckbox(Checkbox):
def draw(self):
print('Drawing a Windows checkbox')

17 Roi Yehoshua, 2025


Example: Mac Concrete Elements
# mac_button.py
from button import Button

class MacButton(Button):
def draw(self):
print('Drawing a Mac button')

# mac_checkbox.py
from checkbox import Checkbox

class MacCheckbox(Checkbox):
def draw(self):
print('Drawing a Mac checkbox')

18 Roi Yehoshua, 2025


Example: GUI Factory Interface
# gui_factory.py
from abc import ABC, abstractmethod
from button import Button
from checkbox import Checkbox

class GUIFactory(ABC):
@abstractmethod
def create_button(self) -> Button:
pass

@abstractmethod
def create_checkbox(self) -> Checkbox:
pass

19 Roi Yehoshua, 2025


Example: Windows UI Factory
# win_factory.py
from gui_factory import GUIFactory
from win_button import WinButton
from win_checkbox import WinCheckbox

class WinFactory(GUIFactory):
def create_button(self) -> WinButton:
return WinButton()

def create_checkbox(self) -> WinCheckbox:


return WinCheckbox()

20 Roi Yehoshua, 2025


Example: Mac UI Factory
# mac_factory.py
from gui_factory import GUIFactory
from mac_button import MacButton
from mac_checkbox import MacCheckbox

class MacFactory(GUIFactory):
def create_button(self) -> MacButton:
return MacButton()

def create_checkbox(self) -> MacCheckbox:


return MacCheckbox()

21 Roi Yehoshua, 2025


Example: Application Configuration
# app_config.py
from gui_factory import GUIFactory
from win_factory import WinFactory
from mac_factory import MacFactory

class AppConfig():
""" Pick the factory type depending on the current configuration or environment settings"""
@staticmethod
def get_factory(os) -> GUIFactory:
if os == 'Windows':
return WinFactory()
elif os == 'Mac':
return MacFactory()
else:
raise ValueError('Unknown operating system')

22 Roi Yehoshua, 2025


Example: Main Script
# [Link]
from app_config import AppConfig Drawing a windows button
Drawing a Windows checkbox
os = 'Windows' # Read from a config file
factory = AppConfig.get_factory(os)

button = factory.create_button()
checkbox = factory.create_checkbox()

[Link]()
[Link]()

23 Roi Yehoshua, 2025


Abstract Factory
 Pros
 Single Responsibility Principle: you can extract the product creation code into one place
 Open/Closed Principle: You can introduce new variants of products without breaking existing
client code
 Cons
 Requires introduction of many new interfaces and classes

24 Roi Yehoshua, 2025


Prototype
 Lets you copy existing objects without making your code dependent on their classes

Client Prototype

+Operation() +Clone()

p=[Link]()

ConcretePrototype1 ConcretePrototype2

+Clone() +Clone()

return copy of this return copy of this

25 Roi Yehoshua, 2025


Copying Objects
 There are two types of copy operations
 Shallow copy is a bit-wise copy of an object
 A new object is created that has an exact copy of the values in the original object
 If the object contains references to other objects, just the reference addresses are copied
 Deep copy copies the object along with all the objects to which it refers (recursively)

26 Roi Yehoshua, 2025


Copying Objects in Python
 The copy module provides shallow and deep copy operations:
 [Link](x) – returns a shallow copy of x
 [Link](x) – returns a deep copy of x

27 Roi Yehoshua, 2025


Prototype Example
 In this example, we’ll use the Prototype pattern to produce exact copies of geometric
objects, without coupling the code to their classes
 A subclass may call the parent’s cloning method before copying its own field values

28 Roi Yehoshua, 2025


Example: Base Prototype
from abc import ABC, abstractmethod

class Shape(ABC):
def __init__(self, location, color):
[Link] = location
[Link] = color

def __str__(self):
return f'Location: {[Link]}, Color: {[Link]}'

@abstractmethod
def clone(self):
pass

29 Roi Yehoshua, 2025


Example: Concrete Prototype
from shape import Shape
import copy

class Circle(Shape):
def __init__(self, location, color, radius):
super().__init__(location, color)
[Link] = radius

def __str__(self):
return super().__str__() + f', Radius: {[Link]}'

def clone(self):
return [Link](self)

30 Roi Yehoshua, 2025


Example: Concrete Prototype
from shape import Shape
import copy

class Rectangle(Shape):
def __init__(self, location, color, width, length):
super().__init__(location, color)
[Link] = width
[Link] = length

def __str__(self):
return super().__str__() + f', Width: {[Link]}, Length: {[Link]}'

def clone(self):
return [Link](self)

31 Roi Yehoshua, 2025


Example: Main Script
from circle import Circle
from rectangle import Rectangle

c1 = Circle((5, 10), 'Red', 10)


c2 = [Link]()

print(c1)
print(c2)

r1 = Rectangle((2, 3), 'Black', 10, 20)


r2 = [Link]()

print(r1)
print(r2)

Location: (5, 10), Color: Red, Radius: 10


Location: (5, 10), Color: Red, Radius: 10
Location: (2, 3), Color: Black, Width: 10, Length: 20
Location: (2, 3), Color: Black, Width: 10, Length: 20

32 Roi Yehoshua, 2025


Prototype Registry
 The prototype registry provides an easy way to access frequently-used prototypes
 It stores a set of pre-built objects that are ready to be copied in a hash map

33 Roi Yehoshua, 2025


Prototype
 Pros:
 You can clone objects without coupling to their concrete classes
 You can avoid repeated initialization code in favor of cloning pre-built prototypes
 You can produce complex objects more conveniently
 Cons:
 Cloning complex objects that have circular references might be tricky

34 Roi Yehoshua, 2025


Builder
 Handles the construction of complex objects step by step
 Allows you to produce different representations of an object using the same
construction code
Director Builder

+Construct() +BuildPart()

foreach item in structure ConcreteBuilder


[Link]() Product

+BuildPart()
+GetResult()

35 Roi Yehoshua, 2025


Builder Example
 We’ll use the builder pattern to build different types of report
 Each report consists of a header, body and footer
 We’ll define two types of reports: a simple TextReport and an HTMLReport

36 Roi Yehoshua, 2025


Example: Report Builder Interface
# report_builder.py
from abc import ABC, abstractmethod

class ReportBuilder(ABC):
@abstractmethod
def build_header(self):
pass

@abstractmethod
def build_body(self):
pass

@abstractmethod
def build_footer(self):
pass

@abstractmethod
def get_report(self) -> str:
pass

37 Roi Yehoshua, 2025


Example: Text Report Builder
# text_report_builder.py
from report_builder import ReportBuilder

class TextReportBuilder(ReportBuilder):
def __init__(self):
[Link] = ""

def build_header(self):
[Link] += "Header of the report\n"

def build_body(self):
[Link] += "Body of the report\n"

def build_footer(self):
[Link] += "Footer of the report\n"

def get_report(self) -> str:


return [Link]

38 Roi Yehoshua, 2025


Example: HTML Report Builder
# html_report_builder.py
from report_builder import ReportBuilder

class HTMLReportBuilder(ReportBuilder):
def __init__(self):
[Link] = ""

def build_header(self):
[Link] += "<h1>Header</h1>\n"

def build_body(self):
[Link] += "<p>Body</p>\n"

def build_footer(self):
[Link] += "<div>footer></div>\n"

def get_report(self) -> str:


return [Link]

39 Roi Yehoshua, 2025


Example: Director
# [Link]
from report_builder import ReportBuilder

class Director:
def construct(self, builder: ReportBuilder) -> str:
builder.build_header()
builder.build_body()
builder.build_footer()

return builder.get_report()

40 Roi Yehoshua, 2025


Example: Main Script
# [Link]
from director import Director Header of the report
from text_report_builder import TextReportBuilder Body of the report
from html_report_builder import HTMLReportBuilder Footer of the report

director = Director() <h1>Header</h1>


text_report = [Link](TextReportBuilder()) <p>Body</p>
print(text_report)
<div>footer></div>
html_report = [Link](HTMLReportBuilder())
print(html_report)

41 Roi Yehoshua, 2025


Builder
 Pros
 You can construct objects step-by-step, defer construction steps or run steps recursively
 You can reuse the same construction code when building various representations of products
 Single Responsibility Principle: You can isolate complex construction code from the business
logic of the product
 Cons
 The overall complexity of the code increases

42 Roi Yehoshua, 2025


Singleton
 Ensures that a class has only one instance, while providing a global access point to it

43 Roi Yehoshua, 2025


Singleton: How to Implement in Python
 We override the __new__() method that controls the creation of new instances
 Normally, __new__ creates a new instance of the class by invoking the superclass’s
__new__() method and then invoking the new instance’s __init__() method
 We intercept the __new__() method and tell it to create only one class instance
# [Link]
class Logger:
_instance = None

def __new__(cls, *args, **kwargs):


if cls._instance is None:
print('Creating a new instance')
cls._instance = super().__new__(cls)
return cls._instance

44 Roi Yehoshua, 2025


Singleton: How to Implement in Python
 The object is created on the first call to the class:
from logger import Logger

log1 = Logger()
print(log1)

Creating a new instance


<[Link] object at 0x00000216B0AE6F40>

 But the second call returns the same instance:


log2 = Logger()
print(log2)

<[Link] object at 0x00000216B0AE6F40>

45 Roi Yehoshua, 2025


Thread-Safe Singleton
 In multithreaded environments, we need to ensure that multiple threads don’t
create the singleton object multiple times
 To reduce overhead of acquiring a lock, the double-checked locking pattern is used
 Locking occurs only if an instance of the class has not been created yet
# therad_safe_logger.py
import threading

class ThreadSafeLogger:
_instance = None
_lock = [Link]()

def __new__(cls, *args, **kwargs):


if cls._instance is None:
with cls._lock:
if cls._instance is None:
print('Creating a new instance')
cls._instance = super().__new__(cls)
return cls._instance

46 Roi Yehoshua, 2025


Singleton
 Pros:
 You can be sure that a class has only a single instance
 You gain a global access point to that instance
 The singleton object is initialized only when it’s requested for the first time (lazy initialization)
 Cons:
 Can mask bad design
 Creates a coupling between all the classes that use the singleton: they share a global variable
 Makes unit testing difficult
 It’s hard to create mock objects of the singleton since you cannot override static methods

47 Roi Yehoshua, 2025

You might also like