设计模式之门面模式(Facade Pattern 外观模式)

news2024/12/28 5:16:49

一、模式定义

门面模式(Facade Pattern):外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。门面模式又称为外观模式,它是一种对象结构型模式。

二、模式动机

现代的软件系统都非常复杂,尽管我们已经想尽一切方法将其“分而治之”,把一个系统划分为好几个较小的子系统了,但是仍然可能会存在这样的问题:子系统内有非常多的类,客户端往往需要和许多对象打交道之后 才能完成想要完成的功能。
在我们的生活中医院就是这样的。一般的医院都会分为挂号、门诊、化验、收费、取药等。看病的病人要想治好自己的病(相当于一个客户端想要实现自己的功能)就要和医院的各个部门打交道。首先,病人需要挂号,然后门诊,如果医生要求化验的话,病人就要去化验,然后再回到门诊室,最后拿药,经过一系列复杂的过程后才能完成看病的过程。如下图所示:

 

 解决这种不便的方式就是引入门面模式。如果我们在医院设立一个接待员的话,病人只负责和接待员接触,由接待员负责与医院的各个部门打交道,如下图所示:

三、模式结构

 

 

从上图中我们可以看出门面模式一共有两种角色:

门面角色:客户端调用这个角色的方法。此角色知晓相关的子系统的功能和责任。正常情况下,本角色会将所有从客户端发来的请求委派到相应的子系统中去。

子系统角色:可以同时有一个或者多个子系统。每个子系统都不是一个单独的类,而是一个类的集合。每一个子系统都可以被客户端直接调用,或者被门面角色直接调用。子系统并不知道门面的存在,罪域子系统而言,门面仅仅是另一个客户端而已。

四、实例分析

这次我们来关注一下土豪的个人生活,话说土豪下班回到家里后首先要做的就是把灯打开,我们假设他一共需要打开三个灯,然后就是打开热水器烧水准备洗澡,在等待的过程还会打开电视机看新闻。如果我们用一般的方法来实现的话,代码就会是下面这个样子。

这是电灯的类,里边有打开的方法。

package com.designpattern.facade;

public class Light {
    public void open(){
        System.out.println("Light has been opened!");
    }
}

这是热水器的类,里边有打开的方法。

package com.designpattern.facade;

public class Heater {
    public void open(){
        System.out.println("Heater has been opened!");
    }
}

这是电视机的类,里边有打开的方法。

package com.designpattern.facade;

public class TV {

    public void open(){
        System.out.println("TV has been opened!");
    }
}

在主函数里就要创建各种对象,并且调用他们的open方法。我们看到主函数为了实现土豪下班回家这一个功能需要和三个电灯,一个热水器和一台电视机打交道,非常的复杂,所以这时候我们就应该使用门面模式。

package com.designpattern.facade;

public class Main {

    public static void main(String[] args){
        Light light1 = new Light();
        Light light2 = new Light();
        Light light3 = new Light();
        Heater heater = new Heater();
        TV tv = new TV();

        /**
         * 需要一步一步的操作
         */
        light1.open();
        light2.open();
        light3.open();
        heater.open();
        tv.open();
    }
}

在门面类中我们创建一个统一的open方法,来调度所有的开关。

package com.designpattern.facade;

public class Facade {

    private Light light1, light2, light3;
    private Heater heater;
    private TV tv;

    public Facade() {
        light1 = new Light();
        light2 = new Light();
        light3 = new Light();
        heater = new Heater();
        tv = new TV();
    }

    public void open() {
        light1.open();
        light2.open();
        light3.open();
        heater.open();
        tv.open();
    }
}

这样在主函数类只需要使用门面类就可以了。

package com.designpattern.facade;

public class Main2 {

    public static void main(String[] args) {
        Facade facade = new Facade();
        /**
         * 一步操作就可以完成所有的准备工作
         */
        facade.open();
    }

}

五、模式的优缺点

优点:

  • 对客户屏蔽子系统组件,减少了客户处理的对象数目并使得子系统使用起来更加容易。通过引入门面模式,客户代码将变得很简单,与之关联的对象也很少。
  • 实现了子系统与客户之间的松耦合关系,这使得子系统的组件变化不会影响到调用它的客户类,只需要调整外观类即可。
  • 降低了大型软件系统中的编译依赖性,并简化了系统在不同平台之间的移植过程,因为编译一个子系统一般不需要编译所有其他的子系统。一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。
  • 只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类。

缺点:

  • 不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性。
  • 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。

六、使用场景

  1. 当要为一个复杂子系统提供一个简单接口时可以使用外观模式。该接口可以满足大多数用户的需求,而且用户也可以越过外观类直接访问子系统。
  2. 客户程序与多个子系统之间存在很大的依赖性。引入外观类将子系统与客户以及其他子系统解耦,可以提高子系统的独立性和可移植性。
  3. 在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。

七、需要注意的几点

一个系统有多个外观类
在外观模式中,通常只需要一个外观类,并且此外观类只有一个实例,换言之它是一个单例类。在很多情况下为了节约系统资源,一般将外观类设计为单例类。当然这并不意味着在整个系统里只能有一个外观类,在一个系统中可以设计多个外观类,每个外观类都负责和一些特定的子系统交互,向用户提供相应的业务功能。

不要试图通过外观类为子系统增加新行为
不要通过继承一个外观类在子系统中加入新的行为,这种做法是错误的。外观模式的用意是为子系统提供一个集中化和简化的沟通渠道,而不是向子系统加入新的行为,新的行为的增加应该通过修改原有子系统类或增加新的子系统类来实现,不能通过外观类来实现。

外观模式与迪米特法则
外观模式创造出一个外观对象,将客户端所涉及的属于一个子系统的协作伙伴的数量减到最少,使得客户端与子系统内部的对象的相互作用被外观对象所取代。外观类充当了客户类与子系统类之间的“第三者”,降低了客户类与子系统类之间的耦合度,外观模式就是实现代码重构以便达到“迪米特法则”要求的一个强有力的武器。

抽象外观类的引入
外观模式最大的缺点在于违背了“开闭原则”,当增加新的子系统或者移除子系统时需要修改外观类,可以通过引入抽象外观类在一定程度上解决该问题,客户端针对抽象外观类进行编程。对于新的业务需求,不修改原有外观类,而对应增加一个新的具体外观类,由新的具体外观类来关联新的子系统对象,同时通过修改配置文件来达到不修改源代码并更换外观类的目的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/530552.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

中国南方Oracle用户组沙龙活动:大环境下的Oracle数据库的机遇与挑战

2023年03月12日(周日)在杭州索菲特西湖大酒店 (浙江省杭州市上城区西湖大道333 号),中国南方Oracle用户组创始人之一:周亮(zhou liang)组织举办了主题为《大环境下的Oracle数据库的机遇与挑战》活动,大约有50名左右的人…

YMatrix 5.0 故障自动转移功能新实现,运维更方便!

前言 分布式数据库一般都实现了数据多副本的存储以保证数据的高可用性。在多副本存储的基础上,通过切换活跃的存储副本来实现故障转移,是常见的做法。 YMatrix 5.0 实现了在数据库集群所有数据分片上的故障自动转移,完全实现了数据库集群的…

一文带你深入了解分布式数据的复制原理!!

在分布式数据系统中,复制是一种重要的能力。简单来说,复制就是将数据的副本存储在多个位置,通常是在不同的服务器或节点上。这样做有几个关键的优点: 使得数据与用户在地理上接近(从而减少延迟)&#xff0…

渗透测试--3.1嗅探欺骗攻击

目录 1.中间人攻击 2. 社会工程学攻击 SET使用实例——建立克隆钓鱼网站收集目标凭证 SET工具集之木马欺骗实战反弹链接 后渗透阶段 钓鱼邮件 总结 1.中间人攻击 中间人攻击(Man-in-the-middle attack,简称MITM)是一种常见的网络攻击…

一文带你完整了解数据的编码!!

数据编码是将数据转换为计算机可读取和处理的二进制格式的过程。在数据存储中,正确的数据编码非常重要,因为它能够保证数据的完整性、可靠性和可读性。 数据编码可以确保数据在存储过程中不会发生错误。通过使用适当的数据编码规则,可以防止…

三十二、自定义镜像

1 、Docker镜像的原理 Docker镜像本质是什么? Docker中一个centos镜像为什么只有200MB,而一个centos操作系统的iso文件要几个G? Docker中一个tomcat镜像为什么有500MB,而一个tomcat安装包只有10多MB? 操作系统组成部分: 计算机组成原理 进程调度子…

OPPO 关停“造芯”业务 ZEKU:一场大胆的尝试的结束

前言 近期,OPPO 关停了其“造芯”业务 ZEKU,结束了其自主研发处理器的尝试。本文将对这一事件进行探讨,分析其背后的原因及其对整个行业的影响。 1. ZEKU的成立与使命 在2019年,OPPO旗下的ZEKU研究所成立,标志着OPP…

Redis缓存穿透、缓存雪崩和缓存击穿

缓存穿透 缓存穿透,是指查询一个缓存和数据库中都没有的数据。正常的使用缓存流程大致是,数据查询先进行缓存查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存。如果数据库…

Centos和Windows之间通过主机名实现相互访问

一、业务需求 在内网环境中,我们想直接通过特定的主机名称去访问我们的服务器,而不用去记忆服务器的IP地址,且不想通过nginx等代理工具或域名配置内容来操作。 二、分析 通常我们可以直接在浏览器中输入【localhost】后按下回车键后就可以访问我们的主机80端口的内容了;那…

计算机网络大作业(Wireshark抓包分析)

实验要求 wireshark的深入学习与掌握,如过滤器的使用,归纳方法通过实验阐述ARP的工作原理利用实验结果分析 ICMP 协议的报文结构字段定义基于实验数据深入分析 TCP 协议的连接过程原理,报文的分片等功能从校园网发起向外网中某 Web 服务器的…

基于正点原子电机实验的pid调试助手代码解析(速度环控制)

这里写目录标题 下位机与PID调试助手传输的原理代码讲解(基于正点原子)解析数据接受和数据发送的底层函数数据接受数据帧格式环形数组以及怎么找到它的帧头位置crc校验 数据发送数据上传函数 通过前两节文章,我已经了解了基本的pid算法,现在在完成了电机…

MatrixGate 5.0 性能再升级,加载速度提升三倍!

前言 数据的加载速度在数据处理和分析领域一直是一个挑战,为应对这一挑战,YMatrix 数据库开发了 mxgate 高速数据加载工具。最近,随着 YMatrix 5.0 的 GA,新版 mxgate 与上一版本(4.8.1)相比,速…

Linux 虚拟机 磁盘扩容

概述 在单台虚拟机上部署了过多服务,导致磁盘使用过度达到98%。 现在扩容提高磁盘容量,增加10G。 现象 df -h df -ih du -s具体步骤 VMware 扩容 关闭虚拟机的情况下执行,类似于生产环境下需要关闭服务器,从而添加硬盘等相关操作…

Liunx基础命令 - rm删除命令

rm命令 – 删除文件或目录 rm命令来自英文单词“remove”的缩写,中文译为“消除”,其功能是用于删除文件或目录,一次可以删除多个文件,或递归删除目录及其内的所有子文件。 rm也是一个很危险的命令,使用的时候要特别…

从BinDiff到0day 在IE中利用CVE-2019-1208

前言 如上所述,CVE-2019-1208是UAF漏洞,这类安全漏洞可以破坏有效数据、引发进程crash、并且可以精心利用最终导致任意代码执行。而对于本文介绍的CVE-2019-1208而言,成功利用此漏洞的攻击者可以获得系统当前用户权限。如果当前用户具有admi…

理解Java虚拟机——JVM

目录 一、初识JVM 二、JVM执行流程 三、内存区域划分(JVM运行时数据区) 3.1 本地方法栈(线程私有) 3.2 程序计数器(线程私有,无并发问题) 3.3 JVM虚拟机栈(线程私有&#xff0…

最全的分布式事务详情,它来啰~

我们首先得理解什么是分布式事务呢?分布式事务是指在分布式系统中,涉及多个计算机或服务器的操作序列,这些操作需要满足一致性和可靠性的要求。每个操作要么全部成功执行,要么全部回滚,以保持数据的一致性和完整性。 …

嵌入式通信协议【Modbus】Modbus TCP的帧格式

一、请求帧格式 Client request:例: 19 B2 00 00 00 06 06 03 00 27 00 02 1、头字节 上面是modbus客户端发出的报文内容,为modbus tcp/ip协议格式,其前面的六个字节为头字节( header handle); 19 B2 00 00 00 06 19 B2 00 00…

3D点云之语义分割(相关官方示例介绍)

之前在博客中提到,会考虑用深度学习来对3D点云进行处理,接下来迈出脚步,先整几个例子来熟悉它。例子原型来源于官网,博主在其基础上做了一些代码修改。 一. 例子参考 1. Keras中的资源 Code examples 2.openvinotoolkit open_…

20230515在亚马逊Amazon扣费之后的申诉

20230515在亚马逊Amazon扣费之后的申诉 2023/5/15 22:56 缘起:使用Amazon的12个月的免费存储桶,然后调用S3功能翻译! 但是,被扣费了! 由于绑定的信用卡是工行的,要求和亚马逊解除绑定,工行给了一…