在學習完適配器模式之後,讓我用一句話來總結之:就是把前一個類拿來用,用到你所希望它做的事。“適配器模式”是一種接口適配技術,可通過某個類來使用另一個接口與之不兼容的類,運用此模式時,兩個類的接口都無須改動。今天看的例子是關於一個頁面生成,以及對標題和段落進行渲染的 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 類的部分,那麼適配器模式歡迎您。