您的位置:首页 > 编程语言 > Python开发

Python的结构型设计模式之适配器模式

2017-04-09 21:13 471 查看
在学习完适配器模式之后,让我用一句话来总结之:就是把前一个类拿来用,用到你所希望它做的事。“适配器模式”是一种接口适配技术,可通过某个类来使用另一个接口与之不兼容的类,运用此模式时,两个类的接口都无须改动。今天看的例子是关于一个页面生成,以及对标题和段落进行渲染的 Page 类。首先是一个 Page 类。

class Page:
def __init__(self, title, renderer):
if not isinstance(renderer, Renderer):
raise TypeError("Excepted object of type Render, got {}".format(type(renderer).__name__))

self.title = title
self.renderer = renderer
self.paragraphs = []

def add_paragraph(self, paragraph):
self.paragraphs.append(paragraph)

def render(self):
self.renderer.header(self.title)
for paragraph in self.paragraphs:
self.renderer.paragraph(paragraph)
self.renderer.footer()除了标题和段落外,它需要知道一个渲染类 Renderer 的实例。

这里用到了 isintance() 来判断前者的类型

而 Renderer 类用来定义三个方法的接口
class Renderer(metaclass=abc.ABCMeta):
@classmethod
def __subclasshook__(Class, Subclass):
if Class is Renderer:
attribute = collections.ChainMap(*(Superclass.__dict__ for Superclass in Subclass.__mro__))
methods = {"header", "paragraph", "footer"}
if all(method in attribute for method in methods):
return True
return NotImplemented你现在可能看不太懂这段代码,但是你需要明白的是这是定义 header、paragraph、footer 三个属性的接口就好。

因为在 Page 里面就是用 isinstance 来判断传入的类是否具有这三个行为。

首先看正常的渲染类:
class TextRenderer:
def __init__(self, width=80, file=sys.stdout):
self.width = width
self.file = file
self.previous = False

def header(self, title):
self.file.write("{0:^{2}}\n{1:^{2}}\n".format(title, "=" * len(title), self.width))

def paragraph(self, text):
if self.previous:
self.file.write("\n")
self.file.write(textwrap.fill(text, self.width))
self.file.write("\n")
self.previous = True

def footer(self):
pass三个方法都实现了,没毛病吧

然后看下一个:
class HtmlWriter:
def __init__(self, file=sys.stdout):
self.file = file

def header(self):
self.file.write("<document html>\n<html>\n")

def title(self, title):
self.file.write("<head><title>{}</title></head>".format(title))

def start_body(self):
self.file.write("<body>\n>")

def body(self, text):
self.file.write("<p>{}</p>".format(text))

def end_body(self):
self.file.write("</body>")

def footer(self):
self.file.write("</html>\n")这个一看,有 header 和 footer ,你说那暂时不用渲染 paragraph 了,只渲染 title 也可以吧?

答案是不行的。

因为其行为和页面渲染器接口所定义的不同。所以不能直接用这个。怎么改呢?

到了这次的重点,创建适配器:把它当作参数,然后对其进行聚合,聚合成我们需要的三个行为:
class HtmlRenderer:
def __init__(self, htmlWriter):
self.htmlWriter = htmlWriter

def header(self, title):
self.htmlWriter.header()
self.htmlWriter.title(title)
self.htmlWriter.start_body()

def paragraph(self, text):
self.htmlWriter.body(text)

def footer(self):
self.htmlWriter.end_body()
self.htmlWriter.footer()看见了吧,把 HtmlWrite 当作参数传进来之后,就可以进行聚合,把你认为能组合到一块的放在一起即可。

这就是适配器。

怎么样,看到这里觉得适配器模式还很简单吧。如果你想用 A 类,又想在别的地方用 A 类的部分,那么适配器模式欢迎您。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: