Decorator Decorators are great tools to add additional features to an existing object without using subclassing.
from functools import wraps
def make_blink(function):
"""Defines the decorator"""
#This makes the decorator transparent in terms of its name and docstring
@wraps(function)
#Define the inner function
def decorator():
#Grab the return value of the function being decorated
ret = function()
#Add new functionality to the function being decorated
return "<blink>" + ret + "</blink>"
return decorator
#Apply the decorator here!
@make_blink
def hello_world():
"""Original function! """
return "Hello, World!"
#Check the result of decorating
print(hello_world())
#Check if the function name is still the same name of the function being decorated
print(hello_world.__name__)
#Check if the docstring is still the same as that of the function being decorated
print(hello_world.__doc__)
Proxy Proxy comes in handy when creating an object that is very resource-intensive. It can postpone object creation unless it's absolutely necessary by creating a placeholder.
import time
class Producer:
"""Define the 'resource-intensive' object to instantiate!"""
def produce(self):
print("Producer is working hard!")
def meet(self):
print("Producer has time to meet you now!")
class Proxy:
""""Define the 'relatively less resource-intensive' proxy to instantiate as a middleman"""
def __init__(self):
self.occupied = 'No'
self.producer = None
def produce(self):
"""Check if Producer is available"""
print("Artist checking if Producer is available ...")
if self.occupied == 'No':
#If the producer is available, create a producer object!
self.producer = Producer()
time.sleep(2)
#Make the prodcuer meet the guest!
self.producer.meet()
else:
#Otherwise, don't instantiate a producer
time.sleep(2)
print("Producer is busy!")
#Instantiate a Proxy
p = Proxy()
#Make the proxy: Artist produce until Producer is available
p.produce()
#Change the state to 'occupied'
p.occupied = 'Yes'
#Make the Producer produce
p.produce()
Adapter This is used when the interfaces are incompatible between a client and a server.
class Korean:
"""Korean speaker"""
def __init__(self):
self.name = "Korean"
def speak_korean(self):
return "An-neyong?"
class British:
"""English speaker"""
def __init__(self):
self.name = "British"
#Note the different method name here!
def speak_english(self):
return "Hello!"
class Adapter:
"""This changes the generic method name to individualized method names"""
def __init__(self, object, **adapted_method):
"""Change the name of the method"""
self._object = object
#Add a new dictionary item that establishes the mapping between the generic method name: speak() and the concrete method
#For example, speak() will be translated to speak_korean() if the mapping says so
self.__dict__.update(adapted_method)
def __getattr__(self, attr):
"""Simply return the rest of attributes!"""
return getattr(self._object, attr)
#List to store speaker objects
objects = []
#Create a Korean object
korean = Korean()
#Create a British object
british =British()
#Append the objects to the objects list
objects.append(Adapter(korean, speak=korean.speak_korean))
objects.append(Adapter(british, speak=british.speak_english))
for obj in objects:
print("{} says '{}'\n".format(obj.name, obj.speak()))
Composite The composite design pattern maintains a tree data structure to represent part-whole relationships. Here we like to build a recursive tree data structure so that an element of the tree can have its own sub-elements.
class Component(object):
"""Abstract class"""
def __init__(self, *args, **kwargs):
pass
def component_function(self):
pass
class Child(Component): #Inherits from the abstract class, Component
"""Concrete class"""
def __init__(self, *args, **kwargs):
Component.__init__(self, *args, **kwargs)
#This is where we store the name of your child item!
self.name = args[0]
def component_function(self):
#Print the name of your child item here!
print("{}".format(self.name))
class Composite(Component): #Inherits from the abstract class, Component
"""Concrete class and maintains the tree recursive structure"""
def __init__(self, *args, **kwargs):
Component.__init__(self, *args, **kwargs)
#This is where we store the name of the composite object
self.name = args[0]
#This is where we keep our child items
self.children = []
def append_child(self, child):
"""Method to add a new child item"""
self.children.append(child)
def remove_child(self, child):
"""Method to remove a child item"""
self.children.remove(child)
def component_function(self):
#Print the name of the composite object
print("{}".format(self.name))
#Iterate through the child objects and invoke their component function printing their names
for i in self.children:
i.component_function()
#Build a composite submenu 1
sub1 = Composite("submenu1")
#Create a new child sub_submenu 11
sub11 = Child("sub_submenu 11")
#Create a new Child sub_submenu 12
sub12 = Child("sub_submenu 12")
#Add the sub_submenu 11 to submenu 1
sub1.append_child(sub11)
#Add the sub_submenu 12 to submenu 1
sub1.append_child(sub12)
#Build a top-level composite menu
top = Composite("top_menu")
#Build a submenu 2 that is not a composite
sub2 = Child("submenu2")
#Add the composite submenu 1 to the top-level composite menu
top.append_child(sub1)
#Add the plain submenu 2 to the top-level composite menu
top.append_child(sub2)
#Let's test if our Composite pattern works!
top.component_function()
Bridge The bridge pattern helps untangle an unnecessary complicated class hierarchy, especially when implementation specific classes are mixed together with implementation-indendent classes. So our problem here is that there are two parallel or orthogonal abstractions. One is implementation-specific, and the other one is implementation-independent.
class DrawingAPIOne(object):
"""Implementation-specific abstraction: concrete class one"""
def draw_circle(self, x, y, radius):
print("API 1 drawing a circle at ({}, {} with radius {}!)".format(x, y, radius))
class DrawingAPITwo(object):
"""Implementation-specific abstraction: concrete class two"""
def draw_circle(self, x, y, radius):
print("API 2 drawing a circle at ({}, {} with radius {}!)".format(x, y, radius))
class Circle(object):
"""Implementation-independent abstraction: for example, there could be a rectangle class!"""
def __init__(self, x, y, radius, drawing_api):
"""Initialize the necessary attributes"""
self._x = x
self._y = y
self._radius = radius
self._drawing_api = drawing_api
def draw(self):
"""Implementation-specific abstraction taken care of by another class: DrawingAPI"""
self._drawing_api.draw_circle(self._x, self._y, self._radius)
def scale(self, percent):
"""Implementation-independent"""
self._radius *= percent
#Build the first Circle object using API One
circle1 = Circle(1, 2, 3, DrawingAPIOne())
#Draw a circle
circle1.draw()
#Build the second Circle object using API Two
circle2 = Circle(2, 3, 4, DrawingAPITwo())
#Draw a circle
circle2.draw()
More Design Patterns in Python
Recommended Posts