python设计模式7外观模式(Facade Pattern)

外观模式(Facade Pattern)又叫门面模式。

随着系统的发展,它们会变得非常复杂,最终会有一个非常大甚至混乱的类和交互的集合,这是很正常的。在许多情况下,我们不希望将这种复杂性暴露给客户。

façade设计模式帮助我们隐藏系统的内部复杂性,并通过简化的接口只向客户端暴露出必要的东西。从本质上讲,façade是在现有的复杂系统上实现的抽象层。

让我们以计算机为例来说明问题。计算机是复杂的机器,依靠几个部分才能完全发挥作用。为了简单起见,"计算机 "这个词在这里指的是使用冯-诺依曼架构的IBM衍生产品。启动计算机是特别复杂的过程。CPU、主内存和硬盘需要启动和运行,启动加载器必须从硬盘加载到主内存,CPU必须启动操作系统内核,等等。我们没有把所有这些复杂的东西暴露给客户,而是创建了封装了整个程序的外观,确保所有的步骤都按照正确的顺序执行。

在对象设计和编程方面,我们应该有几个类,但只有计算机类需要暴露给客户端代码。例如,客户端只需要执行计算机类的start()方法,而其他所有复杂的部分都由计算机类来处理。

真实世界的例子

当你给银行或公司打电话时,你通常会先被连接到客户服务部门。客户服务部的员工在你和实际的部门(账单、技术支持、一般援助等)之间充当门面,而这个员工将帮助你解决你的具体问题。

开启汽车或摩托车的钥匙也可以被认为是一个门面。它是激活内部非常复杂的系统的简单方法。当然,其他复杂的电子设备也是如此,我们可以用一个按钮来激活它们,比如电脑。

在软件方面,django-oscar-datacash模块是一个Django第三方模块,与DataCash支付网关集成。该模块有一个网关类,提供对各种DataCash API的细粒度访问。在此基础上,它还提供了一个façade类,提供了粒度较小的API(对于那些不想弄乱细节的人来说),并且能够为审计目的保存交易。

应用

不把系统的内部功能暴露给客户代码,给我们带来了额外的好处:我们可以对系统进行修改,但客户代码仍然不知道这些修改,也不会受到影响。不需要对客户端代码进行修改。

如果你的系统中有多层,Façade也很有用。你可以为每层引入Façade入口点,并让所有的层通过它们的Façade相互通信。这促进了松耦合,并使各层尽可能保持独立。

实现

多服务器操作系统有一个最小的内核,称为微内核,它以特权模式运行。系统的所有其他服务都遵循服务器架构(驱动服务器、进程服务器、文件服务器,等等)。每个服务器都属于不同的内存地址空间,并在用户模式下运行于微内核之上。这种方法的优点是,操作系统可以变得更加容错、可靠和安全。例如,由于所有的驱动程序都在驱动服务器上以用户模式运行,一个驱动程序的错误不能使整个系统崩溃,也不能影响其他的服务器。这种方法的缺点是性能开销和系统编程的复杂性,因为服务器和微内核之间以及独立服务器之间的通信是通过消息传递进行的。消息传递比Linux等单片机内核中使用的共享内存模型更加复杂。

我们从一个服务器接口开始。一个Enum参数描述了一个服务器的不同可能状态。我们使用ABC模块禁止直接实例化服务器接口,并使基本的boot()和kill()方法成为强制性的,假设在启动、杀死和重启每个服务器时需要采取不同的行动。

class Server(metaclass=ABCMeta): 
    @abstractmethod 
    def __init__(self): 
        pass 
 
    def __str__(self): 
        return self.name 
 
    @abstractmethod 
    def boot(self): 
        pass 
 
    @abstractmethod  
    def kill(self, restart=True): 
        pass 

模块化的操作系统可以有服务器:文件服务器、进程服务器、认证服务器、网络服务器、图形/窗口服务器,等等。下面的例子包括两个存根服务器:FileServer和ProcessServer。除了需要由服务器接口实现的方法外,每个服务器都可以有自己的特定方法。例如,FileServer有create_file()方法用于创建文件,而ProcessServer有create_process()方法用于创建进程。

class FileServer(Server): 
    def __init__(self): 
        '''actions required for initializing the file server''' 
        self.name = 'FileServer' 
        self.state = State.new 
 
    def boot(self): 
        print(f'booting the {self}') 
        '''actions required for booting the file server''' 
        self.state = State.running 
 
    def kill(self, restart=True): 
        print(f'Killing {self}') 
        '''actions required for killing the file server''' 
        self.state = State.restart if restart else State.zombie 
 
    def create_file(self, user, name, permissions): 
        '''check validity of permissions, user rights, etc.''' 
        print(f"trying to create the file '{name}' for user '{user}' with permissions {permissions}") 
 
class ProcessServer(Server): 
    def __init__(self): 
        '''actions required for initializing the process server''' 
        self.name = 'ProcessServer' 
        self.state = State.new 
 
    def boot(self): 
        print(f'booting the {self}') 
        '''actions required for booting the process server''' 
        self.state = State.running 
 
    def kill(self, restart=True): 
        print(f'Killing {self}') 
        '''actions required for killing the process server''' 
        self.state = State.restart if restart else State.zombie 
 
    def create_process(self, user, name): 
        '''check user rights, generate PID, etc.''' 
        print(f"trying to create the process '{name}' for user '{user}'") 

OperatingSystem 类是外观。在其 init() 中,所有必要的服务器实例都被创建。客户端代码使用的start()方法,是系统的入口。如果有必要,可以添加更多的封装方法,作为服务器服务的访问点,如封装器,create_file()和create_process()。从客户的角度来看,所有这些服务都是由OperatingSystem类提供的。客户端不应该被一些不必要的细节所迷惑,如服务器的存在和每个服务器的责任。

OperatingSystem类的代码如下。

class OperatingSystem: 
    '''The Facade''' 
    def __init__(self): 
        self.fs = FileServer() 
        self.ps = ProcessServer() 
 
    def start(self): 
        [i.boot() for i in (self.fs, self.ps)] 
 
    def create_file(self, user, name, permissions): 
        return self.fs.create_file(user, name, permissions) 
 
    def create_process(self, user, name): 
        return self.ps.create_process(user, name) 
 
def main(): 
    os = OperatingSystem() 
    os.start()  
    os.create_file('foo', 'hello', '-rw-r-r') 
    os.create_process('bar', 'ls /tmp') 
 
if __name__ == '__main__': 
    main()

小结

在本章中,我们已经学会了如何使用façade模式。这种模式是为客户代码提供简单接口的理想选择,这些客户代码想要使用一个复杂的系统,但不需要意识到系统的复杂性。一台计算机有一个外观,因为我们使用它所需要做的就是按下按钮来打开它。所有其余的硬件复杂性都由BIOS、启动加载器和系统软件的其他组件透明地处理。还有更多现实生活中的例子,比如当我们连接到银行或公司的客户服务部门,以及我们用来开启车辆的钥匙。

我们讨论了一个使用Façade的Django第三方模块:django-oscar-datacash。它使用façade模式来提供一个简单的DataCash API和保存交易的能力。

我们介绍了façade的基本用例,并以多服务器操作系统使用的接口的实现来结束本章。façade是一种隐藏系统复杂性的优雅方式,因为在大多数情况下,客户端代码不应该知道这些细节。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容