【编程学习】适配器模式&抽象类-打造代码的优雅之道

适配器模式

所谓适配器模式就是将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。其中有几个重要概念:

  • 目标类(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()

# output:
# execute the mac computer program
# play a synthesizer

此外还有一个实现方案,那就是对所有被适配类构建一个统一的适配器类:

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()

# output:
# execute the mac computer program
# play a synthesizer

抽象类

可以看到,我再定义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 #Animal.register(Cat)
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__()])

# output:
# True True
# True True
# ['Cat']

运行instance后发现,实例化以后的DogCat确实是属于Animal类;运行issubclass后也证明DogCat属于Animal的子类。但是,用遍历的方式查看Animal的子类时,发现其中只有Cat,也就是只有继承的子类而没有注册的子类。