Design Patterns 1 - Adapter Pattern
Feb 7, 2021 00:00 · 503 words · 3 minute read
Adapter design pattern helps you unify almost all classes.
1. Why design patterns?
Being good at problem-solving is one thing but to take your programming skill to the next level, one must know how complex software projects are architected. Software design patterns provide templates and tricks used to design and solve recurring software problems and tasks. Being well-versed in knowledge of design patterns allows one to spot brittle and immature code from miles away. There are three common design pattern types:
- Creational design patterns: all about class instantiation or object creation. Class-creation patterns use inheritance effectively in the instantiation process, object-creation patterns use delegation effectively to get the job done.
- Structural design patterns: organize different classes and objects to form larger structures and provide new functionality.
- Behavioral design patterns: identify common communication patterns between objects and realize these patterns.
2. What is adapter pattern?
The adapter pattern is a structural design pattern that helps us make two incompatible interfaces compatible. In the following example, we created three classes Club
, Musician
and Dancer
with different methods. Eventually, we’d like to call them through a single class Adapter
. This is done by an important usage of class self.__dict__.update()
, which udpates the functions within the class.
3. Code example
The UML digram of this example is as follow:
class Club:
def __init__(self, name):
self.name = name
def __str__(self):
return f'the club {self.name}'
def organize_event(self):
return 'hires an artist to perform for the people'
class Musician:
def __init__(self, name):
self.name = name
def __str__(self):
return f'the musician {self.name}'
def play(self):
return 'plays music'
class Dancer:
def __init__(self, name):
self.name = name
def __str__(self):
return f'the dancer {self.name}'
def dance(self):
return 'does a dance performance'
class Adapter:
def __init__(self, obj, adapted_methods):
self.obj = obj
self.__dict__.update(adapted_methods)
def __str__(self):
return str(self.obj)
# Main function
objects = [Club('Jazz Cafe'), Musician('Roy Ayers'), Dancer('Shane Sparks')]
for obj in objects:
if hasattr(obj, 'play') or hasattr(obj, 'dance'):
if hasattr(obj, 'play'):
adapted_methods = dict(organize_event=obj.play)
elif hasattr(obj, 'dance'):
adapted_methods = dict(organize_event=obj.dance)
# referencing the adapted object here
obj = Adapter(obj, adapted_methods)
print(f'{obj} {obj.organize_event()}')
# Output:
the club Jazz Cafe hires an artist to perform for the people
the musician Roy Ayers plays music
the dancer Shane Sparks does a dance performance
As the above code shows the Adapter
class unifies all other three classes even they have different subfunction within the classes.
4. Code explanation
Note that the self.__dict__()
function includes all the attributes or functions within the corresponding class as a dictionary. Updating the dictionary corresponds to update the class. In above code, the last obj
represents the Dancer
class. Therefore, the self.obj
is Dancer
, and the self.organize_event
is Dancer.dance
.
obj.__dict__
{'obj': <__main__.Dancer at 0x10ecd12d0>,
'organize_event': <bound method Dancer.dance of <__main__.Dancer object at 0x10ecd12d0>>}
The __str__
is used to return the corresponding string when call print(obj)
, such as:
obj
<__main__.Adapter at 0x10ecd1590>
print(obj)
the dancer Shane Sparks
Lastly, the dict(organize_event=obj.play)
is same as {'organize_event': obj.play}
. For example,
dict(a=2)
{'a': 2}
Reference:
- Ayeva, Kamon; Kasampalis, Sakis. “Mastering Python Design Patterns.”
- geeksforgeeks software design pattern
- sorucemaking design pattern