DEV Community

Cover image for Design Pattern in Python (4): Adapter Pattern
Z. QIU
Z. QIU

Posted on

Design Pattern in Python (4): Adapter Pattern

Today I would like to do some coding work on Adapter Pattern in Python.
Adapter is one of the Structural Patterns.

It works as a bridge between two incompatible interfaces. This pattern involves a single class which is responsible to join functionalities of independent or incompatible interfaces.

Alt Text

I found easily one real life example of "adapter" in my room. I have a laptop of brand Redmi which has a chinese type power plug. I am living in France and thus I have only European type socket at home. It's not possible to insert directly my laptop's plug into a french socket.
Alt Text

To solve the issue, I am using an adapter to charge my laptop. This small gadget is really very practical for me.
Alt Text

Thus I would like to code this example in Python today. Let's go!

1st simulation: Incompatible issue

Socket simulation

Firstly I define several classes for sockets including PowerSocket base class and its concrete classes.

class PowerSocket():
    """
       PowerSocket base class 
    """
    def __init__ (self, holeNum, Shape, Volt):
        self.__num_holes = holeNum
        self.__hole_shape = Shape
        self.__volt = Volt

    def getHoleNum (self):
        return self.__num_holes 

    def getHoleShape (self):
        return self.__hole_shape 

    def getVolt (self):
        return self.__volt 

### some concrete PowerSocket classes
class chineseSocket(PowerSocket):
    def __init__ (self):
        super().__init__( 3, "FLAT", 220)  
class europeanSocket(PowerSocket):
    def __init__ (self):
        super().__init__( 2, "ROUND", 220)          
class taiwaneseSocket(PowerSocket):
    def __init__ (self):
        super().__init__( 2, "FLAT", 110) 
Enter fullscreen mode Exit fullscreen mode

Laptop simulation

Now a class of my Laptop with its plug class. Method charge() of RedmiLaptop class shall check if socket is compatible with its power plug.

class chinise3pinPlug():
    def __init__ (self):
        self.pins = 3
        self.volt = 220
        self.pinshape = "FLAT"    

class RedmiLaptop():

    def __init__ (self):
        self.plug = chinise3pinPlug()


    def charge(self, socket, powerInWatt):
        res = False     
        if (isinstance(socket, PowerSocket)):
            res = (self.plug.pins == socket.getHoleNum() )  and \
            (self.plug.pinshape == socket.getHoleShape() ) and  \
            (self.plug.volt == socket.getVolt() )
        else:
            print ("Socket is not instance of PowerSocket")

        if res:
            current = round(powerInWatt / self.plug.volt, 2)
            print("Start charging... Power: {} watt; Socket current: {} am ...".format(str(powerInWatt), str(current)))
        else:
            print("Socket and plug not compatible, impossible to charge.")
        return res
Enter fullscreen mode Exit fullscreen mode

Charging simulation

Now launch the simulation. I move to 3 different areas in this simulation and see if I can charge my laptop there.

if __name__ == "__main__":

    laptop = RedmiLaptop() # instance of my Redmi Laptop

    # I am in china mainland
    chSocket = chineseSocket()
    laptop.charge(socket=chSocket, powerInWatt=235)

    # I am in France
    euSocket = europeanSocket()
    laptop.charge(socket=euSocket, powerInWatt=235)

    # I am in Taipei 
    twSocket = taiwaneseSocket()
    laptop.charge(socket=twSocket, powerInWatt=235)
Enter fullscreen mode Exit fullscreen mode

Execution output:
Alt Text

2nd Simulation: adapter usage

Adapter simulation

Now I introduce a SocketAdapter base class as adapter interface for socket conversion. I define a AnyToChineseAdapter concrete class to simulate a multi-usage converter for chinese-type plug of my laptop. AnyToChineseAdapter has a output socket of chinese type. It implements a core method convert() which is responsible to convert different socket interfaces to chinese type, namely it bridges various socket types to chinese-type plugs.

class SocketAdapter():
    """
       SocketAdapter base class 
    """
    def __init__ (self ):
        pass

    def convert(self ):
        pass

    def getSocket (self):
        pass


class AnyToChineseAdapter(SocketAdapter):
    """
       A concrete SocketAdapter class that can convert any socket to chinese socket
    """

    def __init__ (self):
        super().__init__()
        self.__outSocket = chineseSocket()
        self.__voltRatio = 1
        self.__plug = ""

    def convert(self, fromSocket):
        res = True
        if  isinstance (fromSocket,  chineseSocket):
            self.__voltRatio = 1
            self.__plug = "Chinese format Plug"
            print("Chinese to Chinese using {}".format(self.__plug))
        elif isinstance (fromSocket,  europeanSocket):
            self.__voltRatio = 1
            self.__plug = "European format Plug"
            print("European to Chinese using {}".format(self.__plug))
        elif isinstance (fromSocket,  taiwaneseSocket):
            self.__voltRatio = 2
            self.__plug = "Taiwanese format Plug"
            print("Taiwanese to Chinese using {}".format(self.__plug))
        # elif     isinstance (fromSocket,  someSocket):
        #    do converting stuff...
        else:        
            print("Unknown socket, cannot choose plug format and volt convertion ratio")
            res = False
        return res

    def getSocket(self):
        return self.__outSocket

    def getVoltRatio(self):
        return self.__voltRatio
Enter fullscreen mode Exit fullscreen mode

Socket simulation

I define PowerSocket base class and its concrete classes. This part is almost the same as in the first simulation. I define one extra class martianSocket for simulating socket on Mars.

class PowerSocket():
    """
       PowerSocket base class 
    """
    def __init__ (self, holeNum, Shape, Volt):
        self.__num_holes = holeNum
        self.__hole_shape = Shape
        self.__volt = Volt

    def getHoleNum (self):
        return self.__num_holes 

    def getHoleShape (self):
        return self.__hole_shape 

    def getVolt (self):
        return self.__volt 


### some concrete PowerSocket classes
class chineseSocket(PowerSocket):
    def __init__ (self):
        super().__init__( 3, "FLAT", 220)
class europeanSocket(PowerSocket):
    def __init__ (self):
        super().__init__( 2, "ROUND", 220)   
class taiwaneseSocket(PowerSocket):
    def __init__ (self):
        super().__init__( 2, "FLAT", 110)  
class martianSocket(PowerSocket):
    def __init__ (self):
        super().__init__( 2, "FLAT", 300)          
Enter fullscreen mode Exit fullscreen mode

Laptop simulation

A modification in Laptop class regarding the 1st simulation is that now it has a private member __adapter which is an instance of AnyToChineseAdapter. A new method addAdapter should be called to attach a SocketAdapter instance.

class chinise3pinPlug():
    def __init__ (self):
        self.pins = 3
        self.volt = 220
        self.pinshape = "FLAT"    

class RedmiLaptop():
    def __init__ (self):
        self.plug = chinise3pinPlug()
        self.__adapter = None

    def addAdapter(self, adpt):
        self.__adapter = adpt

    def charge(self, inSocket, powerInWatt):
        res = False   
        if (isinstance(inSocket, PowerSocket)) :
            if self.__adapter.convert(inSocket):
                socket = self.__adapter.getSocket()
                res = (self.plug.pins == socket.getHoleNum() )  and \
                (self.plug.pinshape == socket.getHoleShape() ) and  \
                (self.plug.volt == socket.getVolt() )
            else:
                res = False 
        else:
            print ("Socket is not instance of PowerSocket")

        if res:
            current = round(powerInWatt / self.plug.volt, 2) * self.__adapter.getVoltRatio()
            print("Start charging... Power: {} watt; Socket current: {} am ...".format(str(powerInWatt), str(current)))
        else:
            print("Socket and plug not compatible, impossible to charge.")
        return res
Enter fullscreen mode Exit fullscreen mode

Charging simulation

Now launch the 2nd simulation. I move firstly to 3 different areas on earth and finally go on Mars in this simulation and see if I can charge my laptop there.

if __name__ == "__main__":

    redmiAd = AnyToChineseAdapter()
    laptop = RedmiLaptop()
    laptop.addAdapter(redmiAd)


    # I am in china mainland
    chSocket = chineseSocket()
    laptop.charge(chSocket, powerInWatt=235)

    # I am in France
    euSocket = europeanSocket()
    laptop.charge(euSocket, powerInWatt=235)

    # I am in Taipei 
    twSocket = taiwaneseSocket() 
    laptop.charge(twSocket, powerInWatt=235)

    # I am on Mars
    msSocket = martianSocket()
    laptop.charge(msSocket, powerInWatt=235)
Enter fullscreen mode Exit fullscreen mode

Execution output:
Alt Text

See, I can charge my laptop with chinese, european and taiwanese sockets. However, I cannot charge it on Mars, since the adapter does not (yet) have conversion method for martian socket type.

Top comments (1)

Collapse
 
sayli6242 profile image
Sayli Patil

this would find me so useful....