Adapter Design Pattern in Python
What is it?
The Adapter Design Pattern helps to convert the interface of a class into another interface that the client expects. For example, an electric kettle with square-pinned plug (client) expects to have a square-pin socket. But the wall-socket (our existing class) has round pins. The pin converter (adapter) comes to the rescue.
Another example, a French-only speaking person (client) wishes to have a two-way communication in French, and hence expects another person to know French. But an English-only speaking person (our existing class) cannot understand French. The English-French translator who knows both French and English (adapter) serves the purpose.
It is classified under Structural Design Patterns as it offers one of the best ways to organize class hierarchy.
Why the need for it: Problem Statement
The Adapter Pattern is needed when the interface of an existing class needs to be morphed into one that the client wants.
Terminology
- Adaptee: The existing class whose interface needs to be morphed.
- Adapter: The class in charge of morphing the interface of the adaptee to cater to client's needs.
- Client: The class who wishes that wants to change the interface of an existing class to suit its own needs.
How to implement it
# Example #1: Language Translator # Our client, a French-only speaking person, wishes to have a two-way conversation in French. He expects the other person to be able to speak French. # Our existing class, an English-only speaking person cannot speak French. # Our Adapter, is in the form of a translator who translates English responses to French so that our French-only speaking client can have his two-way conversation in French. # Existing Class: a.k.a. Adaptee: Incompatible interface # 1 class EnglishSpeaker: def responseToGreeting(self): return "Hello to you too!" def responseToFarewell(self): return "Goodbye my friend." # Adapter Class, which takes functionality provided by EnglishSpeaker, morphs it into functionality expected by the FrenchSpeaker. class Translator: '''Accespts an english speaker, translates his responses to French.''' _englishSpeaker = None _englishToFrenchPhrases = { "Hello to you too!": "Bonjour à vous aussi", "Goodbye my friend.": "Au revoir mon ami" } def __init__(self, englishSpeaker): self._englishSpeaker = englishSpeaker # Client: Incompatible interface # 2 class FrenchSpeaker: '''Accepts an English-To-French Speaker as argument.''' _englishToFrenchTranslator = None def __init__(self, englishToFrenchTranslator): self._englishToFrenchTranslator = englishToFrenchTranslator def exchangeGreetings(self): print("Salut!") print( self._englishToFrenchTranslator._englishToFrenchPhrases[ self._englishToFrenchTranslator._englishSpeaker.responseToGreeting() ] ) def exchangeFarewell(self): print("Au revoir!") print( self._englishToFrenchTranslator._englishToFrenchPhrases[ self._englishToFrenchTranslator._englishSpeaker.responseToFarewell() ] ) # Create an English Speaking person englishSpeaker = EnglishSpeaker() # Create a translator with popular english phrases englishToFrenchTranslator = Translator(englishSpeaker) # The French Speaking Person can now get responses in French frenchSpeaker = FrenchSpeaker(englishToFrenchTranslator) # Two-way conversation in French frenchSpeaker.exchangeGreetings() frenchSpeaker.exchangeFarewell() # OUTPUT Salut! Bonjour à vous aussi Au revoir! Au revoir mon ami
# Example #2: Square-pin to round-pin adapter # Consider two incompatible interfaces: round-pin Socket & square-pin Electric Kettle. # The Electric Kettle expects a square-pin connection for it to work. # The Adapter takes square-pin of Electric Kettle, and connects to round-pin socket. # Existing Class: a.k.a. Adaptee: Incompatible interface # 1 class Socket: _pinType = "Round" # The Adapter: acts as an interface between two incompatible interfaces class Adapter: _socket = None _pinType = "SquareToRound" def __init__(self, socket): self._socket = socket # Client: Incompatible interface # 2 class ElectricKettle: _adapter = None _pinType = "Square" def __init__(self, adapter): self._adapter = adapter def makeTea(self): if self._adapter._pinType == (self._pinType + "To" + self._adapter._socket._pinType): # "SquareToRound" == "Square" + "To" + "Round" print("Boiling water....") print("Adding ingredients...") print("Tea brewing...") print("Tea is ready!") else: print("No power. Can't function.") # Create a socket roundPinSocket = Socket() # Connect adapter and socket squareToRoundAdapter = Adapter(roundPinSocket) # Connect Kettle and socket via adapter. kettle = ElectricKettle(squareToRoundAdapter) kettle.makeTea() # OUTPUT: Boiling water.... Adding ingredients... Tea brewing... Tea is ready!
Related to: Bridge & Decorator
- Creational Patterns
- Factory
- Abstract Factory
- Prototype
- Singleton
- Builder
- Architectural Pattern
- Model View Controller (MVC)