Structural Pattern
Structural desing pattern is concerned with class and object composition. They use inheritance to compose interfaces and declare ways to compose object to get the desired new functionality. Lets look at some of the examples of Structural pattern in this article.
Adaptor desgin pattern:
This pattern allows incompatible interfaces to work together by wrapping the interfaces around an already existing class. This can be verified via following example:
class EuropeanSocketInterface :
def voltage ( self ): pass
def live ( self ): pass
def neutral ( self ): pass
def earth ( self ): pass
# adaptee
class Socket ( EuropeanSocketInterface ):
def voltage ( self ):
return 230
def live ( self ):
return 1
def neutral ( self ):
return - 1
def earth ( self ):
return 0
class USASocketInterface :
def voltage ( self ): pass
def live ( self ): pass
def neutral ( self ): pass
# this is the adaptor pattern
class Adaptor ( USASocketInterface ):
def __init__ ( self , interface ):
self . __interface = interface
def voltage ( self ):
return 110
def live ( self ):
return self . __interface . live ()
def neutral ( self ):
return self . __interface . neutral ()
# client
class ClientElectricKettle :
def __init__ ( self , obj ):
self . __power = obj
def boil ( self ):
if self . __power . voltage () > 110 :
print ( "Too hot" )
elif self . __power . live () == 1 and self . __power . neutral () == - 1 :
print ( "Coffee time" )
else :
print ( "No power" )
if __name__ == "__main__" :
socket = Socket ()
adaptor = Adaptor ( socket )
client = ClientElectricKettle ( adaptor )
client . boil ()
print ( "end" )
Output:
Coffee time
end
Facade desgin pattern:
This pattern provides simplified interfaces to a large body of interfaces.Let’s look at an example below:
class _IgnitionSystem ( object ):
@ staticmethod
def produce_spark ():
return True
class _Engine ( object ):
def __init__ ( self ):
self . revs_per_minute = 0
def turnon ( self ):
self . revs_per_minute = 2000
def turnoff ( self ):
self . revs_per_minute = 0
class _FuelTank ( object ):
def __init__ ( self , level = 30 ):
self . _level = level
@ property
def level ( self ):
return self . _level
@ level . setter
def level ( self , level ):
self . _level = level
class _DashBoardLight ( object ):
def __init__ ( self , is_on = False ):
self . _is_on = is_on
def __str__ ( self ):
return self . __class__ . __name__
@ property
def is_on ( self ):
return self . _is_on
@ is_on . setter
def is_on ( self , status ):
self . _is_on = status
def status_check ( self ):
if self . _is_on :
print ( "{}: ON" . format ( str ( self )))
else :
print ( "{}: OFF" . format ( str ( self )))
class _HandBrakeLight ( _DashBoardLight ):
pass
class _FogLampLight ( _DashBoardLight ):
pass
class _Dashboard ( object ):
def __init__ ( self ):
self . lights = { "handbreak" : _HandBrakeLight (), "fog" : _FogLampLight ()}
def show ( self ):
for light in self . lights . values ():
light . status_check ()
# Facade
class Car ( object ):
def __init__ ( self ):
self . ignition_system = _IgnitionSystem ()
self . engine = _Engine ()
self . fuel_tank = _FuelTank ()
self . dashboard = _Dashboard ()
@ property
def km_per_litre ( self ):
return 17.0
def consume_fuel ( self , km ):
litres = min ( self . fuel_tank . level , km / self . km_per_litre )
self . fuel_tank . level -= litres
def start ( self ):
print ( " \n Starting..." )
self . dashboard . show ()
if self . ignition_system . produce_spark ():
self . engine . turnon ()
else :
print ( "Can't start. Faulty ignition system" )
def has_enough_fuel ( self , km , km_per_litre ):
litres_needed = km / km_per_litre
if self . fuel_tank . level > litres_needed :
return True
else :
return False
def drive ( self , km = 100 ):
print ( " \n " )
if self . engine . revs_per_minute > 0 :
while self . has_enough_fuel ( km , self . km_per_litre ):
self . consume_fuel ( km )
print ( "Drove {}km" . format ( km ))
print ( "{:.2f}l of fuel still left" . format ( self . fuel_tank . level ))
else :
print ( "Can't drive. The Engine is turned off!" )
def park ( self ):
print ( " \n Parking..." )
self . dashboard . lights [ "handbreak" ]. is_on = True
self . dashboard . show ()
self . engine . turnoff ()
def switch_fog_lights ( self , status ):
print ( " \n Switching {} fog lights..." . format ( status ))
boolean = True if status == "ON" else False
self . dashboard . lights [ "fog" ]. is_on = boolean
self . dashboard . show ()
def fill_up_tank ( self ):
print ( " \n Fuel tank filled up!" )
self . fuel_tank . level = 100
# the main function is the Client
def main ():
car = Car ()
car . start ()
car . drive ()
car . switch_fog_lights ( "ON" )
car . switch_fog_lights ( "OFF" )
car . park ()
car . fill_up_tank ()
car . drive ()
car . start ()
car . drive ()
if __name__ == "__main__" :
main ()
Output:
Starting...
_HandBrakeLight: OFF
_FogLampLight: OFF
Drove 100km
24.12l of fuel still left
Drove 100km
18.24l of fuel still left
Drove 100km
12.35l of fuel still left
Drove 100km
6.47l of fuel still left
Drove 100km
0.59l of fuel still left
Switching ON fog lights...
_HandBrakeLight: OFF
_FogLampLight: ON
Switching OFF fog lights...
_HandBrakeLight: OFF
_FogLampLight: OFF
Parking...
_HandBrakeLight: ON
_FogLampLight: OFF
Fuel tank filled up!
Can't drive. The Engine is turned off!
Starting...
_HandBrakeLight: ON
_FogLampLight: OFF
Drove 100km
94.12l of fuel still left
Drove 100km
88.24l of fuel still left
Drove 100km
82.35l of fuel still left
Drove 100km
76.47l of fuel still left
Drove 100km
70.59l of fuel still left
Drove 100km
64.71l of fuel still left
Drove 100km
58.82l of fuel still left
Drove 100km
52.94l of fuel still left
Drove 100km
47.06l of fuel still left
Drove 100km
41.18l of fuel still left
Drove 100km
35.29l of fuel still left
Drove 100km
29.41l of fuel still left
Drove 100km
23.53l of fuel still left
Drove 100km
17.65l of fuel still left
Drove 100km
11.76l of fuel still left
Drove 100km
5.88l of fuel still left
Drove 100km
0.00l of fuel still left
Decorator desgin pattern:
This pattern dynamically adds/overrides existing method to add new functionality without changing it’s calling style. Let’s look at an example below. In this example we have two files
decorator.py
implement.py
The decorator.py file has the decorator design pattern code and the implement.py file uses decorator.py method to get Cost, Ingredient, Sales tax for various items.
Code in decorator.py
import six
from abc import ABCMeta
@ six . add_metaclass ( ABCMeta )
class AbstractCoffee :
def get_ingridents ( self ):
pass
def get_price ( self ):
pass
def get_taxes ( self ):
return 0.1 * self . get_price ()
class ConcreteCoffee ( AbstractCoffee ):
def get_ingridents ( self ):
return 'coffee'
def get_price ( self ):
return 100
@ six . add_metaclass ( ABCMeta )
class AbstractCoffeeDecorator ( AbstractCoffee ):
def __init__ ( self , decorated_coffee ):
self . decorated_coffee = decorated_coffee
def get_ingridents ( self ):
return self . decorated_coffee . get_ingridents ()
def get_price ( self ):
return self . decorated_coffee . get_price ()
class Suger ( AbstractCoffeeDecorator ):
def get_ingridents ( self ):
return self . decorated_coffee . get_ingridents () + " ,suger"
def get_price ( self ):
return self . decorated_coffee . get_price () + 25
class Milk ( AbstractCoffeeDecorator ):
def get_ingridents ( self ):
return self . decorated_coffee . get_ingridents () + " ,milk"
def get_price ( self ):
return self . decorated_coffee . get_price () + 75
class Vanilla ( AbstractCoffeeDecorator ):
def get_ingridents ( self ):
return self . decorated_coffee . get_ingridents () + " ,vanilla"
def get_price ( self ):
return self . decorated_coffee . get_price () + 200
Code in implement.py
import decorator
my_coffee = decorator . ConcreteCoffee ()
print (
'Ingredients :' , my_coffee . get_ingridents (),
', Cost :' , my_coffee . get_price (),
', Sales Tax :' , my_coffee . get_taxes ()
)
my_coffee = decorator . Milk ( my_coffee )
print (
'Ingredients :' , my_coffee . get_ingridents (),
', Cost :' , my_coffee . get_price (),
', Sales Tax :' , my_coffee . get_taxes ()
)
my_coffee = decorator . Vanilla ( my_coffee )
print (
'Ingredients :' , my_coffee . get_ingridents (),
', Cost :' , my_coffee . get_price (),
', Sales Tax :' , my_coffee . get_taxes ()
)
my_coffee = decorator . Suger ( my_coffee )
print (
'Ingredients :' , my_coffee . get_ingridents (),
', Cost :' , my_coffee . get_price (),
', Sales Tax :' , my_coffee . get_taxes ()
)
Now, let’s run implement.py and see the output
Output:
Ingredients : coffee , Cost : 100 , Sales Tax : 10.0
Ingredients : coffee ,milk , Cost : 175 , Sales Tax : 17.5
Ingredients : coffee ,milk ,vanilla , Cost : 375 , Sales Tax : 37.5
Ingredients : coffee ,milk ,vanilla ,suger , Cost : 400 , Sales Tax : 40.0
Proxy desgin pattern:
This pattern provides a placeholder for another object to control access, reduce cost, and reduce complexity. Let’s look at an example below.
class Image :
def __init__ ( self , filename ):
self . _filename = filename
def load_image ( self ):
print ( 'Loading...' , self . _filename )
def display_image ( self ):
print ( 'display...' , self . _filename )
class Proxy :
def __init__ ( self , subject ):
self . _subject = subject
self . _proxystate = None
class ImageProxy ( Proxy ):
def display_image ( self ):
if self . _proxystate is None :
self . _subject . load_image ()
self . _proxystate = 1
print ( "Displaying ..." , self . _subject . _filename )
img1 = ImageProxy ( Image ( "IMAGE_SUB_1" ))
img2 = ImageProxy ( Image ( "IMAGE_SUB_2" ))
img1 . display_image ()
img1 . display_image ()
img2 . display_image ()
img2 . display_image ()
img1 . display_image ()
img1 . display_image ()
Output:
Loading... IMAGE_SUB_1
Displaying ... IMAGE_SUB_1
Displaying ... IMAGE_SUB_1
Loading... IMAGE_SUB_2
Displaying ... IMAGE_SUB_2
Displaying ... IMAGE_SUB_2
Displaying ... IMAGE_SUB_1
Displaying ... IMAGE_SUB_1
In the next lession we will look at some behaviour design patterns.