• The adapter pattern
The adapter pattern is a structural design pattern that helps us make two incompatible interfaces compatible. What does that really mean? If we have an old component and we want to use it in a new system, or a new component that we want to use in an old system, the two can rarely communicate without requiring any code changes. But changing the code is not always possible, either because we don’t have access to it, or because it is impractical. In such cases, we can write an extra layer that makes all the required modifications for enabling communication between the two interfaces. This layer is called an adapter.
In general, if you want to use an interface that expects function_a(), but you only have function_b(), you can use an adapter to convert (adapt) function_b() to function_a().
class OldPaymentSystem: def __init__(self, currency): self.currency = currency
def make_payment(self, amount): print(f"[OLD] Pay {amount} {self.currency}") class NewPaymentGateway: def __init__(self, currency): self.currency = currency
def execute_payment(self, amount): print(f"Execute payment of {amount} {self.currency}") class PaymentAdapter: def __init__(self, system): self.system = system
def make_payment(self, amount): self.system.execute_payment(amount) def main(): new_system = NewPaymentGateway("euro") print(new_system) adapter = PaymentAdapter(new_system) adapter.make_payment(100)
Implementing the adapter pattern – adapt several classes into a unified interface
Let’s look at another application to illustrate adaptation: a club’s activities. Our club has two main activities:
• Hire talented artists to perform in the club
• Organize performances and events to entertain its clients
At the core, we have a Club class that represents the club where hired artists perform some evenings. The organize_performance() method is the main action that the club can perform. The code
is as follows:
class Club: def __init__(self, name): self.name = name def __str__(self): return f"the club {self.name}" def organize_performance(self): return "hires an artist to perform"
Most of the time, our club hires a DJ to perform, but our application should make it possible to organize a diversity of performances: by a musician or music band, by a dancer, a one-man or one-woman show, and so on.
Via our research to try and reuse existing code, we find an open source contributed library that brings us two interesting classes: Musician and Dancer. In the Musician class, the main action is performed by the play() method. In the Dancer class, it is performed by the dance() method.
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"
The code we are writing, to use these two classes from the external library, only knows how to call the organize_performance() method (on the Club class); it has no idea about the play() or dance() methods (on the respective classes).
How can we make the code work without changing the Musician and Dancer classes?
Adapters to the rescue! We create a generic Adapter class that allows us to adapt a number of objects with different interfaces into one unified interface. The obj argument of the __init__() method is the object that we want to adapt, and adapted_methods is a dictionary containing key/value pairs matching the method the client calls and the method that should be called. The code for that class is as follows:
class Adapter: def __init__(self, obj, adapted_methods): self.obj = obj self.__dict__.update(adapted_methods) def __str__(self): return str(self.obj)
When dealing with the instances of the different classes, we have two cases:
• The compatible object that belongs to the Club class needs no adaptation. We can treat it as is.
• The incompatible objects need to be adapted first, using the Adapter class.
The result is that the client code can continue using the known organize_performance() method on all objects without the need to be aware of any interface differences. Consider the following main() function code to prove that the design works as expected:
def main(): 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_performance=obj.play ) elif hasattr(obj, "dance"): adapted_methods = dict( organize_performance=obj.dance ) obj = Adapter(obj, adapted_methods) print(f"{obj} {obj.organize_event()}")
• The decorator pattern
• The bridge pattern
• The facade pattern
• The flyweight pattern
• The proxy pattern
标签:__,obj,name,Python,self,Patterns,Design,class,def From: https://www.cnblogs.com/zhangzhihui/p/18363047