概念解析
适配模式,指的是:将一个类的接口变成客户端所期望的另一种接口,从而使得原本因接口不匹配而无法一起工作的两个类能够一起工作。即适配模式的作用是:
- 接口转换,将原有的接口(或方法)转换成另一个接口
- 用新的接口包装一个已有的类
- 匹配一个老的组件到一个新的接口
适配模式主要存在三个角色:
- 目标(Target):即你期望的目标接口,要转换成的接口
- 源对象(Adaptee):即要转换的角色,要把谁转换为目标角色
- 适配器(Adapter):适配模式的核心角色,负责把源对象转换和包装成目标对象
适配模式类图如下:
其优点在于:
- 可以将两个无关联的类一起运行,起到中间转换的作用
- 提高类的复用率
- 灵活性好,不会破坏原有系统
缺点在于:
- 如果原有系统没有设计好(如:Target不是抽象类或接口,而是一个实体类),适配模式很难实现
- 过多地使用适配器,容易使得代码混乱:如看到调用的是A接口,实际上内部调用的是B接口
设计模板
适配模式的框架模型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from abc import ABCMeta, abstractmethod
class Target(metaclass=ABCMeta):
"""目标类"""
@abstractmethod
def function(self):
pass
class Adaptee():
"""源对象类"""
def special_function(self):
print("被适配对象的特殊功能")
class Adapter(Adaptee, Target):
"""适配器"""
def function(self):
print("进行功能转换")
实例分析
场景说明,设计一个兼容pdf的阅读器,原始阅读器默认支持txt/epub文本格式,需要添加pdf的支持,pdf文件解析可以借助第三方库,需要在原始基础上对pdf文件解析接口进行适配:
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
from abc impot ABCMeta, abstractmethod
import os
class Page():
"""电子书一页的内容"""
def __init__(self, pagenum):
self.__pagenum = pagenum
def get_content(self):
return "第" + str(self.__pagenum) + " 页的内容..."
class Catelogue():
"""目录结构"""
def __init__(self, title):
self.__title = title
self.__chapters = []
def add_chapter(self, title):
self.__chapters.append(title)
def show_info(self):
print("书名:" + self.__title)
print("目录:")
for chapter in self.__chapters:
print(" " + chapter)
class IBook(metaclass=ABCMeta):
"""电子书文档的接口类"""
@abstractmethod
def parse_file(self, file_path):
"""解析文档"""
pass
@abstractmethod
def get_catalogue(self):
"""获取目录"""
pass
@abstractmethod
def get_page_count(self):
"""获取页数"""
pass
@abstractmethod
def get_page(self, pagenum):
"""获取第pagenum页的内容"""
pass
class TxtBook(IBook):
"""TXT解析类"""
def parse_file(self, file_path):
# 模拟文档的解析
print(file_path, " 文件解析成功")
self.__title = os.path.splitext(file_path)[0]
self.__page_count = 500
return True
def get_catalogue(self):
catalogue = Catalogue(self.__title)
catalogue.add_chapter("第一章 标题")
catalogue.add_chapter("第二章 标题")
return catalogue
def get_page_count(self):
return self.__page_count
def get_page(self, pagenum):
return Page(pagenum)
class EpubBook(IBook):
"""Epub解析类"""
# ...代码省略
class Outline():
"""第三方PDF解析库的目录类"""
def __init__(self):
self.__outlines = []
def add_outline(self, title):
self.__outlines.append(title)
def get_outlines(self):
return self.__outlines
class PdfPage():
"""PDF页"""
def __init__(self, pagenum):
self.__pagenum = pagenum
def get_pagenum(self):
return self.__pagenum
class ThirdPdf:
"""第三方PDF解析库"""
def __init__(self):
self.__page_size = 0
self.__title = ""
def open(self, file_path):
print("第三方库解析PDF文件:" + file_path)
self.__title = os.path.splitext(file_path)[0]
self.__page_size = 1000
return True
def get_tile(self):
return self.__title
def get_outline(self):
outline = Outline()
outline.add_outline("第一章 PDF 电子书标题")
outline.add_outline("第二章 PDF 电子书标题")
return outline
def page_size(self):
return self.__page_size
def page(self, index):
return PdfPage(index)
class PdfAdapterBook(ThirdPdf, IBook):
"""对第三方PDF解析库重新进行包装"""
def __init__(self, thirdPdf):
self.__third_pdf = ThirdPdf
def parse_file(self, file_path):
# 模拟文档的解析
rtn = self.__third_pdf.open(file_path)
if rtn:
print(file_path, + "文件解析成功")
return rtn
def get_catalogue(self):
outline = self.get_outline()
print("将Outline结构的目录转换成Catalogue结构的目录")
catalogue = Catalogue(self.__third_pdf.get_title())
for title in outline.get_outlines():
catalogue.add_chapter(title)
return catalogue
def get_page_count(self):
return self.__third_pdf.page_size()
def get_page(self, pagenum):
page = self.page(page_num)
print("将PdfPage的面对象转换成Page的对象")
return Page(page.get_pagenum())