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

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

2017-01-05 19:14 537 查看

适配器模式

适配器模式是一种接口适配技术,可以通过某个类来使用另一个接口与之不相容的类,运用此模式时,两个类中的接口都无需改动。例如,我们想把某个类从原先的应用场景中拿出来放在另一个环境下运行,而这个类又不能修改,那可以考虑适配器模式。

书上的例子:

假设有一个Page类用于渲染页面, 需要知道标题、正文段落以及“渲染器类的实例”。该例子将举出两个渲染器,其中TextRenderer具有相关接口,而HtmlWriter不具有相关接口,需要编写HtmlRenderer适配器类来适配接口。



顶层调用

def main():
paragraph1 = MESSAGE.format("plain-text", "TextRenderer")
paragraph2 = """This is another short paragraph just so that we can
see two paragraphs in action."""
title = "Plain Text"
textPage = Page(title, TextRenderer(22))
textPage.add_paragraph(paragraph1)
textPage.add_paragraph(paragraph2)
textPage.render()

print()

paragraph1 = MESSAGE.format("HTML", "HtmlRenderer")
title = "HTML"
file = sys.stdout
htmlPage = Page(title, HtmlRenderer(HtmlWriter(file)))
htmlPage.add_paragraph(paragraph1)
htmlPage.add_paragraph(paragraph2)
htmlPage.render()

try:
page = Page(title, HtmlWriter())
page.render()
print("ERROR! rendering with an invalid renderer")
except TypeError as err:
print(err)


Page类实现

class Page:

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

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


说明

要保证__init()__函数收到的确实是一个Renderer实例,可以使用assert isinstance(renderer,Renderer)。此方法有两个缺陷,一方面抛出的是AssertionError,而不是我们希望的TypeError。另一方面如果执行程序的时候使用-o(optimize,优化),则会忽略assert语句,导致AttributeError。于是使用如上代码的验证方式。但是也存在一个问题,就是传入的实例必须继承自Renderer基类。在Java或者c++中确实是这样,但是在python中可以采用另一种做法,既能像抽象基类那样检查接口是否匹配,又能像动态类型那样灵活,换句话说,可以在无需继承特定基类的前提下,创建出符合某套接口的对象。

Renderer基类的实现

isinstance()会调用__subclasshook__(Class,Subclass)来决定函数的首个参数是不是第二个参数的子类,或者是Subclass列表中某个类的子类,这里通过重写__subclasshook__(Class,Subclass),来实现上述说明中的情况

class Renderer(mateclass=abc.ABCMeta):
@classmethod
def __subclasshook__(Class, Subclass):
#判断确实是不是在Renderer上调用的,如果不是就返回NotImplement,并沿着继承体系按照通常的规则继续判定下去,这里会导致子类无法继承
if Class is Renderer:
#collections.ChainMap()将所有传进去的映射表当做一张表来看待,__mro__返回Subclass的包括其本身以及其超类的元组。
attributes = collections.ChainMap(*(Superclass.__dict__ for Superclass in Subclass.__mro__))
methods = ("header","paragraph","footer")
if all(method in attributes for method in methons):
return True
return NotImplemented


符合接口要求的TextRenderer

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


不符合接口要求的HtmlWriter

class Htm
4000
lWriter:

def __init__(self, file=sys.stdout):
self.file = file

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

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

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

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

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

def footer(self):
self.file.write("</html>\n")


创建HtmlRenderer,适配Renderer接口

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()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  python 设计模式 技术