[go: up one dir, main page]

0% found this document useful (0 votes)
36 views9 pages

Structural Design Patterns

The document outlines seven structural design patterns: Adapter, Bridge, Composite, Decorator, Facade, Flyweight, and Proxy. Each pattern is described with its purpose, use cases, and examples demonstrating their implementation in code. These patterns help in organizing and managing complex systems by providing flexible and reusable solutions to common design problems.

Uploaded by

Thuy Ho Thi Ngoc
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)
36 views9 pages

Structural Design Patterns

The document outlines seven structural design patterns: Adapter, Bridge, Composite, Decorator, Facade, Flyweight, and Proxy. Each pattern is described with its purpose, use cases, and examples demonstrating their implementation in code. These patterns help in organizing and managing complex systems by providing flexible and reusable solutions to common design problems.

Uploaded by

Thuy Ho Thi Ngoc
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
You are on page 1/ 9

STRUCTURAL DESIGN PATTERNS

1. Adapter
Adapter (Also known as: Wrapper) is a structural design pattern that allows
objects with incompatible interfaces to collaborate.

Use:
Use the Adapter class when you want to use some existing class, but its
interface isn’t compatible with the rest of your code.
Use the pattern when you want to reuse several existing subclasses that lack
some common functionality that can’t be added to the superclass.
Example: SquarePeg and RoundHole
class Target:
def request(self):
return "Target: Default behavior"

class Adaptee:
def specific_request(self):
return "Adaptee: Specific behavior"

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

def request(self):
return f"Adapter: (TRANSLATED) {self.adaptee.specific_request()}"

adaptee = Adaptee()
adapter = Adapter(adaptee)
print(adapter.request()) # Adapter: (TRANSLATED) Adaptee: Specific
behavior
2. Bridge
Bridge is a structural design pattern that lets you split a large class or a set of
closely related classes into two separate hierarchies—abstraction and
implementation—which can be developed independently of each other.
Use:
Use the Bridge pattern when you want to divide and organize a monolithic
class that has several variants of some functionality (for example, if the class
can work with various database servers).
Use the pattern when you need to extend a class in several orthogonal
(independent) dimensions.
Use the Bridge if you need to be able to switch implementations at runtime.
Example: Remote Control and Devices
class Device:
def turn_on(self):
pass

class TV(Device):
def turn_on(self):
return "TV: Turned on"

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

def press_power(self):
return self.device.turn_on()

tv = TV()
remote = RemoteControl(tv)
print(remote.press_power()) # TV: Turned on
3. Compisite
Composite (Also known as: Object Tree) is a structural design pattern that
lets you compose objects into tree structures and then work with these
structures as if they were individual objects.
Use:
Use the Composite pattern when you have to implement a tree-like object
structure.
Use the pattern when you want the client code to treat both simple and
complex elements uniformly.
Example: Tree Structure of Components
class Component:
def operation(self):
pass

class Leaf(Component):
def __init__(self, name):
self.name = name

def operation(self):
return f"Leaf: {self.name}"

class Composite(Component):
def __init__(self):
self.children = []

def add(self, component):


self.children.append(component)

def operation(self):
results = [child.operation() for child in self.children]
return f"Composite: [{', '.join(results)}]"

leaf1 = Leaf("A")
leaf2 = Leaf("B")
tree = Composite()
tree.add(leaf1)
tree.add(leaf2)
print(tree.operation()) # Composite: [Leaf: A, Leaf: B]
4. Decorator
Decorator is a structural design pattern that lets you attach new behaviors to
objects by placing these objects inside special wrapper objects that contain the
behaviors.
Use:
Use the Decorator pattern when you need to be able to assign extra behaviors
to objects at runtime without breaking the code that uses these objects.
Use the pattern when it’s awkward or not possible to extend an object’s
behavior using inheritance.
Example: Notifier with Email and SMS
class Notifier:
def send(self):
return "Sending basic notification"

class EmailDecorator(Notifier):
def __init__(self, notifier):
self.notifier = notifier

def send(self):
return f"{self.notifier.send()} + Email"

class SMSDecorator(Notifier):
def __init__(self, notifier):
self.notifier = notifier

def send(self):
return f"{self.notifier.send()} + SMS"

notifier = Notifier()
email = EmailDecorator(notifier)
sms = SMSDecorator(email)
print(sms.send()) # Sending basic notification + Email + SMS
5. Facade
Facade is a structural design pattern that provides a simplified interface to a
library, a framework, or any other complex set of classes.
Use:
Use the Facade pattern when you need to have a limited but straightforward
interface to a complex subsystem.
Use the Facade when you want to structure a subsystem into layers.
Example: Computer Startup
class CPU:
def freeze(self): print("CPU freeze")

class Memory:
def load(self): print("Memory loading")

class HardDrive:
def read(self): print("Reading from disk")

class ComputerFacade:
def __init__(self):
self.cpu = CPU()
self.memory = Memory()
self.hdd = HardDrive()

def start(self):
self.cpu.freeze()
self.memory.load()
self.hdd.read()

computer = ComputerFacade()
computer.start()
# Output:
# CPU freeze
# Memory loading
# Reading from disk
6. Flyweight
Flyweight (Also known as: Cache) is a structural design pattern that lets you
fit more objects into the available amount of RAM by sharing common parts of
state between multiple objects instead of keeping all of the data in each object.
Use:
Use the Flyweight pattern only when your program must support a huge
number of objects which barely fit into available RAM.
Example: Forest with Tree Types
class TreeType:
def __init__(self, name, color):
self.name = name
self.color = color

def draw(self, x, y):


print(f"Draw {self.name} tree at ({x}, {y}) with color {self.color}")
class TreeFactory:
_tree_types = {}

@classmethod
def get_tree_type(cls, name, color):
key = (name, color)
if key not in cls._tree_types:
cls._tree_types[key] = TreeType(name, color)
return cls._tree_types[key]

factory = TreeFactory()
tree1 = factory.get_tree_type("Oak", "Green")
tree2 = factory.get_tree_type("Oak", "Green")
print(tree1 is tree2) # True
tree1.draw(10, 20) # Draw Oak tree at (10, 20) with color Green
7. Proxy
Proxy is a structural design pattern that lets you provide a substitute or
placeholder for another object. A proxy controls access to the original object,
allowing you to perform something either before or after the request gets
through to the original object.
Use:
Lazy initialization (virtual proxy). This is when you have a heavyweight
service object that wastes system resources by being always up, even though
you only need it from time to time.
Access control (protection proxy). This is when you want only specific
clients to be able to use the service object; for instance, when your objects are
crucial parts of an operating system and clients are various launched
applications (including malicious ones).
Local execution of a remote service (remote proxy). This is when the service
object is located on a remote server.
Logging requests (logging proxy). This is when you want to keep a history
of requests to the service object.
Caching request results (caching proxy). This is when you need to cache
results of client requests and manage the life cycle of this cache, especially if
results are quite large.
Smart reference. This is when you need to be able to dismiss a heavyweight
object once there are no clients that use it.
Example: Virtual Proxy for Image Loading
class RealImage:
def __init__(self, filename):
self.filename = filename
self.load_from_disk()

def load_from_disk(self):
print(f"Loading {self.filename}")

def display(self):
print(f"Displaying {self.filename}")

class ProxyImage:
def __init__(self, filename):
self.filename = filename
self.real_image = None

def display(self):
if not self.real_image:
self.real_image = RealImage(self.filename)
self.real_image.display()

image = ProxyImage("photo.jpg")
image.display() # Loading + Displaying
image.display() # Only Displaying

You might also like