dc1y

笃行信道,自强不息


  • 首页

  • 关于

  • 归档

【Python 与模式】外观模式

发表于 2018-04-03 | 分类于 python , 设计模式

提示:本文译自《Python 3 Object-oriented Programming, Second Edition》,Chapter 11: Python Design Patterns II。

外观模式旨在为一个复杂的组件系统提供一个简单的接口。对于复杂的任务,我们可能必须跟这些对象直接交互,但这样的系统通常有一些 “典型” 用法,并不需要复杂的交互。外观模式允许我们定义一个新对象,用来封装系统的典型用法。每次需要访问常用功能时,可使用这个对象的简化接口。若项目的其它部分需要访问更为复杂的功能,仍然可以直接与系统交互。外观模式的 UML 图完全依赖于子系统,但大致如下:

外观模式

外观对象在许多方面类似于适配器。主要区别是外观试图从复杂的接口中抽象出一个简化接口,而适配器只是尝试把一个现有的接口映射到另一个。

让我们来为一个 email 应用编写一个简单的外观。如我们在第 7 章所见,在 Python 中发送 email 的底层库相当复杂。而两个接收消息的库甚至更糟。

要是有一个简单的类就好了,它可让我们发送邮件,并列出 IMAP 或 POP3 连接上的当前收件箱中的邮件。为保持示例简短,我们采用了 IMAP 和 SMTP:这是两个处理 email 的完全不同的子系统。我们的外观只执行两个任务:发送一封邮件到指定地址,以及检查 IMAP 连接上的收件箱。它做了一些常见的假设,例如 SMTP 和 IMAP 的主机为同一地址,两者的用户名和密码相同,并且都使用了标准端口。这个外观覆盖了许多邮件服务器的场景,但若某个程序员需要更灵活的方式,仍可绕过外观并直接访问这两个子系统。

外观类使用了邮件服务器的主机名、登录用的用户名和密码来初始化:

import smtplib
import imaplib

class EmailFacade:
def __init__(self, host, username, password):
self.host = host
self.username = username
self.password = password

send_email 方法格式化邮件地址和消息,并使用 smtplib 进行发送。这个任务并不复杂,但需要把传入外观的 “自然” 参数进行一些处理,以便以正确的格式提供给 smtplib 进行发送:

def send_email(self, to_email, subject, message):
if not "@" in self.username:
from_email = "{0}@{1}".format(self.username, self.host)
else:
from_email = self.username
message = ("From: {0}\r\n"
"To: {1}\r\n"
"Subject: {2}\r\n\r\n{3}").format(
from_email,
to_email,
subject,
message)
smtp = smtplib.SMTP(self.host)
smtp.login(self.username, self.password)
smtp.sendmail(from_email, [to_email], message)

方法开头的 if 语句,用于检查 username 是完整的发件人邮箱地址,还是只有 @ 左边部分;不同的主机的登录细节会有区别。

最后,获取当前收件箱中的消息的代码相当混乱;IMAP 协议过度设计了,而 imaplib 标准库只是该协议的一层薄封装:

def get_inbox(self):
mailbox = imaplib.IMAP4(self.host)
mailbox.login(bytes(self.username, 'utf8'),
bytes(self.password, 'utf8'))
mailbox.select()
x, data = mailbox.search(None, 'ALL')
messages = []
for num in data[0].split():
x, message = mailbox.fetch(num, '(RFC822)')
messages.append(message[0][1])
return messages

现在,把这些代码放在一起,我们就有了一个简单的外观类,可用相当直观的方式来发送和接收消息,比与这些复杂的库直接交互要简单得多。

尽管在 Python 社区中很少提及,但外观模式是 Python 生态不可或缺的一部分。因为 Python 强调语言的可读性,语言本身及其库倾向于为复杂的任务提供易于理解的接口。例如,for 循环、list 推导及生成器等,都是较为复杂的迭代器协议的外观。defaultdict 的实现是一个外观,它剥离了字典中不存在某个键的边际条件。第三方的 requests 库是一个强大的外观,封装了可读性较差的 HTTP 请求库。

【Python 与模式】适配器模式

发表于 2018-04-03 | 分类于 python , 设计模式

提示:本文译自《Python 3 Object-oriented Programming, Second Edition》,Chapter 11: Python Design Patterns II。

与我们在第 8 章了解到的大多数模式不同,适配器模式用于与现有的代码交互。我们不会设计一组全新的、实现了适配器模式的对象。适配器用于让两个预先存在的对象一起工作,即使它们的接口并不兼容。与可让 VGA 投影仪插入到 HDMI 端口的显示适配器一样,适配器对象居于两个不同接口之间,并在它们之间进行实时转换。适配器对象的唯一目的就是执行这些转换。适配时可能涉及许多任务,如转换参数格式、重新安排参数次序、调用命名不同的方法,或者是提供默认参数等。

在结构方面,适配器模式类似于简化的装饰器模式。装饰器一般提供它们所替换的同名接口,而适配器则在两个接口间进行映射。下面是 UML 图:

适配器模式

阅读全文 »

【Python 与模式】策略模式

发表于 2018-04-02 | 分类于 python , 设计模式

提示:本文译自《Python 3 Object-oriented Programming, Second Edition》,Chapter 10: Python Design Patterns I。

模板模式对于移除重复代码很有用;它是一种支持第 5 章所讨论的 DRY(Don’t Repeat Yourself)原则的实现。它是为几个要完成的任务有着一些共同步骤的情况而设计的。这些共同步骤在基类中实现,而不同的步骤则在子类中重写,以提供自定义的行为。在某种程度上,它类似于泛化的策略模式,但算法的相似部分使用一个基类来共享。下面是该模式的 UML 图:

模板模式

阅读全文 »

【Python 与模式】单例模式

发表于 2018-04-02 | 分类于 python , 设计模式

提示:本文译自《Python 3 Object-oriented Programming, Second Edition》,Chapter 10: Python Design Patterns I。

单例模式是最有争议的模式;许多人指其为一种 “反模式”,即应避免采用和发扬的模式。在 Python 中,若有人使用了单例模式,则几乎肯定是犯了错,因为他们来自于某种限制较严的编程语言。

那还有什么讨论的必要呢?单例是最出名的设计模式之一。它在极度面向对象的语言中很有用,也是传统面向对象编程的关键部件。更贴切地说,单例背后的概念很有用,即使我们在 Python 中以完全不同的方式来实现这个概念。

单例模式背后的基本概念,是让某个对象只能有唯一的实例存在。一般来说,这个对象是某种类似于我们在第 5 章讨论的管理器类。这种对象通常需要被大范围的其它对象引用,并把管理器对象的引用传递给各种需要它的方法和构造器,这样可能让代码变得难于阅读。

相反,若使用了单例,则各对象从单例类中请求管理器对象的单一实例,因此无需将其引用四处传递。下面的 UML 图未能完全描述这个过程,但在此提供以确保完整性:

单例模式

阅读全文 »

【Python 与模式】状态模式

发表于 2018-04-02 | 分类于 python , 设计模式

提示 :本文译自《Python 3 Object-oriented Programming, Second Edition》,Chapter 10: Python Design Patterns I。

状态模式在结构上类似于策略模式,但其意图和目的则区别很大。状态模式的目标是表达一个状态迁移系统:对象可明显处于某种特定状态,且某些动作可将其推向另一状态的系统。

在状态模式中,我们需要一个管理器,或者是上下文类来提供一个切换状态的接口。在内部,这个类包含一个指向当前状态的指针;每个状态都知道可以进入其余的哪些状态,并根据所发生的动作来迁移到这些状态。

因此,我们需要两种类,上下文类及多个状态类。上下文类维护当前的状态,并把动作委托给状态类。相对于调用上下文的对象来说,状态类一般是隐藏的;就像一个在内部执行状态管理的黑匣子。下面是状态模式的 UML 图:

状态模式

阅读全文 »

【Python 与模式】策略模式

发表于 2018-03-31 | 分类于 python , 设计模式

提示:本文译自《Python 3 Object-oriented Programming, Second Edition》,Chapter 10: Python Design Patterns I。

在面向对象编程中,策略模式常用于作为抽象的演示。这种模式针对一个问题实现了不同的解决方案,每个解决方案都是一个不同的对象。客户端代码可在运行时,动态地选择最合适的实现。

一般情况下,不同的算法有不同的权衡;某个算法可能比另一个算法更快,但使用更多的内存,而第三个算法则可能最适合于多 CPU 或分布式系统。下面是策略模式的 UML 图:

策略模式

阅读全文 »

【Python 与模式】观察者模式

发表于 2018-03-31 | 分类于 python , 设计模式

提示:本文译自《Python 3 Object-oriented Programming, Second Edition》,Chapter 10: Python Design Patterns I。

观察者模式对于状态监测及事件处理等情况非常有用。这种模式允许一个对象被一组未知的、动态的 “观察者” 对象监测。

当核心对象的一个值发生改变时,它将通过调用 update() 方法来通知所有观察者对象变化已经发生。每个观察者则负责在核心对象变化时执行不同的任务;核心对象并不知道、也不关心这些任务是什么,而观察者通常也不知道或关心其它观察者在做什么。

下面是观察者模式的 UML 图:

观察者模式

阅读全文 »
12

czq

Python, Hexo, Git

13 日志
4 分类
16 标签
© 2018 czq
由 Hexo 强力驱动
|
主题 — NexT.Gemini v6.0.6