适配器模式
所谓适配器模式就是将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。其中有几个重要概念:
- 目标类(Target):定义客户所需的接口,可以是一个抽象类或接口,也可以是具体类
- 适配器类(Adapter):转换器,通过调用另一个接口对Adaptee和Target进行适配
- 适配者类(Adaptee):被适配类,包括了客户希望的业务方法
实现示例
我们来通过一个案例来看看适配器模式具体做了什么。
首先我们定义一些用户可能会用到的对象,比如一个电脑Computer
和一个音响Synthesizer
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class Computer: def __init__(self,name): self._name=name
@property def name(self): return self._name
@name.setter def name(self,value): self._name=value
def __str__(self): return 'the {} computer'.format(self._name)
def execute(self): print(f'execute the {self._name} computer program') class Synthesizer: def __init__(self,name): self._name=name
def __str__(self): return 'the {} synthesizer'.format(self._name) def play(self): print('play a synthesizer')
|
但是显然,其中使用电脑时,我们需要调用execute
方法,而使用音响时,我们需要调用play
方法,这样的不一致会给用户带来一定的困扰,如何解决?那就给两个东西构建一个适配器吧!
那么我们需要定义一个Target
类,定义其中给用户需要调用的接口是execute
方法,也就是被适配类不管是用什么方法调用,我们都希望将其转化为execute
方法
1 2 3 4
| class Target(metaclass=abc.ABCMeta): @abc.abstractmethod def execute(self): pass
|
然后分别给两个对象构建适配器(这里因为Computer
已经是execute
方法了,就不需要为其定义适配器):
1 2 3 4 5
| class SynthesizerAdapter(Target): def __init__(self,syn): self.syn=syn def execute(self): self.syn.play()
|
让我们运行一下看看结果,此时我们对每一个被适配类调用execute
方法就可以实现它们的功能了:
1 2 3 4 5 6 7 8 9 10 11 12
| objects = [] objects2.append(Computer('mac'))
syn = Synthesizer('yamaha') objects2.append(SynthesizerAdapter(syn))
for obj in objects: obj.execute()
|
此外还有一个实现方案,那就是对所有被适配类构建一个统一的适配器类:
1 2 3 4 5 6 7
| class Adapter: def __init__(self,adp_obj,adp_methods): self.obj=adp_obj self.__dict__.update(adp_methods)
def __str__(self): return str(self.obj)
|
在这里,我们直接构建一个Adapter
类,然后在实例化时为其传入想要的方法作为execute
的返回结果,就不用为每一个被适配类实现一个适配器类了。
让我们运行一下看看结果,此时我们对每一个被适配类调用execute
方法就可以实现它们的功能了:
1 2 3 4 5 6 7 8 9 10 11 12
| objects = [] objects2.append(Computer('mac'))
syn = Synthesizer('yamaha') objects2.append(Adapter(syn,{'execute':syn.play}))
for obj in objects: obj.execute()
|
抽象类
可以看到,我再定义Target
类时,用到了metaclass=abc.ABCMeta
的一个参数,这就是python中常用的抽象类的方法。
所谓抽象类就是一种特殊的类,只能被继承,不能被实例化,我们需要从不同的类中抽取相同的属性和行为作为抽象类。
抽象类与普通类有什么区别呢?
- 抽象类中有抽象方法
- 不能被实例化,只能被继承
- 子类必须实现抽象方法
抽象类需要借助abc模块来实现,比如我们想要定义一个动物类,动物们都有吃饭、喝水、跑步的方法:
1 2 3 4 5 6 7 8 9 10 11
| import abc class Animal(metaclass=abc.ABCMeta): @abc.abstractmethod def eat(self, food): pass @abc.abstractmethod def drink(self, water): pass @abc.abstractmethod def run(self): pass
|
我们没有办法直接实例化这个Animal
类,只能从Animal
继承或者注册一个类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class Cat(Animal): def eat(self, food): print(f"The cat is eating {food}") def drink(self, water): print(f"The cat is eating {water}") def run(self): print(f"The cat is running")
@Animal.register class Dog: def eat(self, food): print(f"The dog is eating {food}") def drink(self, water): print(f"The dog is eating {water}") def run(self): print(f"The dog is running")
|
那继承和注册的区别在哪里?,我们运行代码看看:
1 2 3 4 5 6 7 8 9 10
| cat = Cat() dog = Dog() print(isinstance(cat, Animal), isinstance(dog, Animal)) print(issubclass(Cat, Animal), issubclass(Dog, Animal)) print([sc.__name__ for sc in Animal.__subclasses__()])
|
运行instance
后发现,实例化以后的Dog
与Cat
确实是属于Animal
类;运行issubclass
后也证明Dog
与Cat
属于Animal
的子类。但是,用遍历的方式查看Animal
的子类时,发现其中只有Cat
,也就是只有继承的子类而没有注册的子类。