设计模式之【外观/门面模式】,不打开这扇门永远不知道门后有多少东西

news2024/10/6 5:57:28

文章目录

  • 一、什么是外观模式(门面模式)
    • 1、外观模式的结构
    • 2、使用场景
    • 3、外观模式的优缺点
    • 4、外观模式注意事项
  • 二、实例
    • 1、外观模式的通用写法
    • 2、智能家居案例
    • 3、积分换礼品案例
  • 参考资料

一、什么是外观模式(门面模式)

外观模式也叫门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体的细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。其主要特征是定义了一个高层接口,让子系统更容易使用,属于结构型模式。

外观(Facade)模式是“迪米特法则”的典型应用。

1、外观模式的结构

在这里插入图片描述

外观模式主要包含2种角色:

  • 外观角色(Facade):也称门面角色,系统对外的统一接口;
  • 子系统角色(SubSystem):可以同时有一个或多个SubSystem。每个SubSystem都不是一个单独的类,而是一个类的集合。SubSystem并不知道Facade的存在,对于SubSystem而言,Facade只是另一个客户端而已(即Facade对SubSystem透明)。

2、使用场景

其实,在我们日常的编码工作中,我们都在有意无意地大量使用外观模式,但凡只要高层模块需要调度多个子系统(2个以上类对象),我们都会自觉地创建一个新类封装这些子系统,提供精简接口,让高层模块可以更加容易间接调用这些子系统的功能。尤其是现阶段各种第三方SDK,各种开源类库,很大概率都会使用外观模式。尤其是你觉得调用越方便的,外观模式使用的一般更多。

  • 对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。
  • 当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。
  • 当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。

3、外观模式的优缺点

优点:

  • 简化了客户端调用过程,无需深入了解子系统,以防给子系统带来风险。
  • 减少系统依赖、松散耦合。
  • 更好地划分访问层次,提高了安全性。
  • 遵循迪米特法则,即最少知道原则。

缺点:

  • 当增加子系统和扩展子系统行为时,可能容易带来未知风险(例如事务)。
  • 不符合开闭原则。
  • 某些情况下可能违背单一职责原则。

4、外观模式注意事项

我们知道,类、模块、系统之间的“通信”,一般都是通过接口调用来完成的。接口设计的好坏,直接影响到类、模块、系统是否好用。所以,我们要多花点心思在接口设计上。我经常说,完成接口设计,就相当于完成了一半的开发任务。只要接口设计得好,那代码就差不到哪里去。

接口粒度设计得太大,太小都不好。太大会导致接口不可复用,太小会导致接口不易用。在实际的开发中,接口的可复用性和易用性需要“微妙”的权衡。针对这个问题,我的一个基本的处理原则是,尽量保持接口的可复用性,但针对特殊情况,允许提供冗余的门面接口,来提供更易用的接口

在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个Facade类,来提供遗留系统的比较清晰简单的接口,让新系统与Facade类交互,提高复用性。

但是,不能过多的或者不合理的使用外观模式,要判断使用外观模式的必要性,使用外观模式是让系统有层次,利于维护的目的。

二、实例

1、外观模式的通用写法

// 外观角色 Facade
public class Facade {
    private SubSystemA a = new SubSystemA();
    private SubSystemB b = new SubSystemB();
    private SubSystemC c = new SubSystemC();

    // 对外接口
    public void do() {
        this.a.doA();
        this.b.doB();
        this.c.doC();
    }
}
// 子系统
public class SubSystemA {
    public void doA() {
        System.out.println("doing A stuff");
    }
}
// 子系统
public class SubSystemB {
    public void doB() {
        System.out.println("doing B stuff");
    }
}
// 子系统
public class SubSystemC {
    public void doC() {
        System.out.println("doing C stuff");
    }
}

当多个子系统都需要同时操作时,使用一个Facade提供对外的一个接口即可:

Facade facade = new Facade();
facade.do(); // doA + doB + doC

2、智能家居案例

小明的爷爷已经60岁了,一个人在家生活:每次都需要打开灯、打开电视、打开空调;睡觉时关闭灯、关闭电视、关闭空调;操作起来都比较麻烦。所以小明给爷爷买了智能音箱,可以通过语音直接控制这些智能家电的开启和关闭。类图如下:

在这里插入图片描述

//灯类
public class Light {
	public void on() {
		System.out.println("打开了灯....");
	}
	public void off() {
		System.out.println("关闭了灯....");
	}
}
//电视类
public class TV {
	public void on() {
		System.out.println("打开了电视....");
	}
	public void off() {
		System.out.println("关闭了电视....");
	}
}
//空调类
public class AirCondition {
	public void on() {
		System.out.println("打开了空调....");
	}
	public void off() {
		System.out.println("关闭了空调....");
	}
}
//智能音箱
public class SmartAppliancesFacade {
	private Light light;
	private TV tv;
	private AirCondition airCondition;
	public SmartAppliancesFacade() {
		light = new Light();
		tv = new TV();
		airCondition = new AirCondition();
	}
	public void say(String message) {
		if(message.contains("打开")) {
			on();
		} else if(message.contains("关闭")) {
			off();
		} else {
			System.out.println("我还听不懂你说的!!!");
		}
	}
	//起床后一键开电器
	private void on() {
		System.out.println("起床了");
		light.on();
		tv.on();
		airCondition.on();
	}
	//睡觉一键关电器
	private void off() {
		System.out.println("睡觉了");
		light.off();
		tv.off();
		airCondition.off();
	}
}
//测试类
public class Client {
	public static void main(String[] args) {
		//创建外观对象
		SmartAppliancesFacade facade = new SmartAppliancesFacade();
		//客户端直接与外观对象进行交互
		facade.say("打开家电");
		facade.say("关闭家电");
	}
}

3、积分换礼品案例

一个商城系统涉及到积分系统、支付系统、物流系统等借口的调用。如果所有的接口调用全部由前端发送网络请求去调用现有接口的话,一则会增加前端开发人员的难度,二则会增加一些网络请求影响页面性能。

这个时候就可以发挥门面模式的优势了。将所有现成的接口全部整合到一个类中,由后端提供统一的接口给前端调用,这样前端开发人员就不需要关心各接口的业务关系,只需要把精力集中在页面交互上。

// 实体类
public class GiftInfo {

    private String name;

    public GiftInfo(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
// 校验积分
public class QualifyService {

    public boolean isAvailable(GiftInfo giftInfo){
        System.out.println("校验" +giftInfo.getName() + "积分通过,库存通过。");
        return true;
    }
}

// 扣减积分
public class PaymentService {

    public boolean pay(GiftInfo giftInfo){
        System.out.println("扣减" + giftInfo.getName() + " 积分成功");
        return true;
    }
}

// 物流系统
public class ShippingService {
    public String delivery(GiftInfo giftInfo){
        System.out.println(giftInfo.getName() + "进入物流系统");
        String shippingNo = "666";
        return shippingNo;
    }
}
// 使用Facade整合
public class FacadeService {
    private QualifyService qualifyService = new QualifyService();
    private PaymentService paymentService = new PaymentService();
    private ShippingService shippingService = new ShippingService();


    public void exchange(GiftInfo giftInfo){
        if(qualifyService.isAvailable(giftInfo)){
            if(paymentService.pay(giftInfo)){
                String shippingNo = shippingService.delivery(giftInfo);
                System.out.println("物流系统下单成功,物流单号是:" + shippingNo);
            }
        }
    }
}

测试代码:

FacadeServicefacadeService = new FacadeService();

GiftInfo giftInfo = new GiftInfo("《Spring 5核心原理》");

facadeService.exchange(giftInfo);

参考资料

http://www.uml.org.cn/sjms/202105262.asp

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

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

相关文章

yoloV5项目工程源码解读(2)(未完成)

概述 将主要从三个部分对源码进行解读。 数据层面,dataloader 和 数据增强网络模型,模型细节和逻辑模型训练,训练策略等 数据源解读 utils 中有,在train.py中能跳到该函数。 train.py中 # Trainloader 创建dataloader就是我们…

网络安全工程师辛苦吗?

“人生如寄,何事辛苦怨斜晖”,意思是人活着就像寄生在这个世界上,为什么一定要劳碌奔波,最后还抱怨人生苦短呢? 但说到辛苦二字,什么工作不辛苦呢?除了体制内的一些工作稍微轻松一些&#xff0c…

打家劫舍问题

题目: 打家劫舍https://leetcode.cn/problems/house-robber/ 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上…

重写Properties类,实现对properties文件的有序读写,数据追加,解决中文乱码

前言 *.properties文件,是 Java 支持的一种配置文件类型,并且 Java 提供了 properties 类来读取 properties 文件中的信息。文件中以键值对 "键值"的形式,存储工程中会多次重复使用的配置信息,通过“Properties”类来读…

【Mysql实战】使用存储过程和计算同比环比

背景 同环比,是基本的数据分析方法。在各类调研表中屡见不鲜,如果人工向前追溯统计数据,可想而知工作量是非常大的。 标题复制10行,并且每行大于10个字符【源码解析】SpringBoot接口参数【Mysql实战】使用存储过程和计算同比环比…

超全总结:硬件设计基础60条

硬件是一个非常复杂的系统,在设计过程中都会遇到或多或少的问题,本文中总结了非常基础的60个问题,供大家参考。 1、请说明一下滤波磁珠和滤波电感的区别。 磁珠由导线穿过铁氧体组成,直流电阻很小,在低频时阻抗也很小…

数字化转型,目的是为了转型还是数字化?

受第四次工业革命浪潮的影响,传统工业经济社会快速向数字经济转型过渡,企业创新面临的经济环境发生根本性变革。数字技术广泛应用于生产、交换、消费等经济环节,为企业产品创新、服务创新以及数字化开放式创新提供了动力源泉。数字经济背景下…

如何利用生产管理系统提高粉末治金工业的生产调度能力

在粉末冶金工业中,生产管理系统的应用已经成为了一个必不可少的部分。生产管理系统可以帮助企业实现自动化、信息化、智能化的生产,提高生产效率、降低生产成本、提高产品质量。生产管理系统可以对生产流程进行全面的监控和管理,从而实现生产…

11个超好用的SVG编辑工具

SVG的优势在于SVG图像可以更加灵活,自由收缩放大而不影响图片的质量,一个合适的SVG编辑工具能够让你的设计事半功倍,下面就一起来看看这些冷门软件好用在哪里。这11个超好用的SVG编辑工具依次为:即时设计、Justinmind、Sketsa SVG…

Sentinel-Dashboard-1.8持久化Nacos

Sentinel-Dashboard-1.8持久化Nacos 目录 Sentinel-Dashboard-1.8持久化Nacos一、客户端改造1.引入pom.xml文件依赖2.配置application.yml文件。 二、Sentinel-Dashboard源码改造三、测试 一、客户端改造 1.引入pom.xml文件依赖 <!-- https://mvnrepository.com/artifact/…

这些神奇的AI智能机器人很早就已出现过,你确定你不了解?

很多人自从ChatGPT出现以后&#xff0c;就总是担忧&#xff0c;担心自己的职业被影响&#xff0c;然后很多人大肆宣扬 ChatGPT 真是了不得&#xff0c;未来再辅助机器人&#xff0c;加上大数据&#xff0c;一定可以怎么怎么样&#xff0c;说的神乎其神&#xff0c;说实话&#…

几个pdf怎么合并在一起?

几个pdf怎么合并在一起&#xff1f;在日常生活和工作中&#xff0c;我们可能会遇到需要将多个PDF文件合并为一个文件的问题。在对PDF文件合并之后&#xff0c;能够更好地组织和管理信息。将pdf文件合并能够在很大程度上提高工作效率&#xff0c;减少查找和打开不同文件的时间。…

【计算机视觉 | ViT-G】谷歌大脑提出 ViT-G:缩放视觉 Transformer,高达 90.45% 准确率

文章目录 一、简介二、如何做到的&#xff1f;三、扩展数据四、「head」 的解耦权重衰减五、通过移除 [class] token 节省内存六、实验结果6.1 将计算、模型和数据一起扩展6.2 ViT-G/14 结果 论文地址为&#xff1a; https://arxiv.org/pdf/2106.04560.pdf一、简介 视觉 Trans…

PoseiSwap合规、隐私与支持更广泛的资产

Nautilus Chain 代表了公链赛道发展的一个新的范式形态&#xff0c;作为目前行业内首个 Layer3 链&#xff0c;是目前行业内第一个并行化且运行速度最快的EVM Rollup 方案。作为首个模块化链&#xff0c;存储、计算、共识等都在不同的模块中&#xff0c;意味着其能够获得更高的…

如何建立DDR3测试工程

要建立DDR3的测试工程&#xff0c;首先要生成mig IP核&#xff0c;然后写测试模块使用这个IP核进行测试。 一、生成 mig IP核 建立一个新工程&#xff0c;然后生成 mig IP核。 关键步骤如下&#xff1a; &#xff08;1&#xff09;点击 IP catalog&#xff0c;在搜索框输入…

SSM框架学习-AOP通知类型

在AOP中&#xff0c;通知&#xff08;Advice&#xff09;是对切点进行操作的方法&#xff0c;用于实现切面定义的具体逻辑。Spring框架支持五种类型的通知&#xff1a; 1. 前置通知&#xff08;Before advice&#xff09; 在连接点执行前&#xff0c;执行通知 Before("**…

【跟着陈七一起学C语言】今天总结:C语言的结构体和其它数据形式

友情链接&#xff1a;专栏地址 知识总结顺序参考C Primer Plus&#xff08;第六版&#xff09;和谭浩强老师的C程序设计&#xff08;第五版&#xff09;等&#xff0c;内容以书中为标准&#xff0c;同时参考其它各类书籍以及优质文章&#xff0c;以至减少知识点上的错误&#x…

OpenGL高级-立方体贴图

运行效果 源代码 着色器 渲染物体的顶点着色器&#xff1a; #version 330 core // 传入局部坐标下的顶点坐标 layout( location 0 ) in vec3 position; layout (location 1) in vec2 texCoords;// 传入变换矩阵 uniform mat4 model; uniform mat4 view; uniform mat4 proje…

d2l的一些理论知识的整理【1】

目录 考试知识整理引言2 预备知识2.1. 数据操作2.2. 数据预处理2.3. 线性代数2.4. 微积分2.5. 自动微分2.6. 概率2.7. 查阅文档 3 线性神经网络3.1. 线性回归3.2. 线性回归的从零开始实现3.3. 线性回归的简洁实现3.4. softmax回归3.5. 图像分类数据集3.6. softmax回归的从零开始…

AT24C16页写和多页写

AT24C16 2K字节(存储内存) 128&#xff08;页面数&#xff09;* 16 &#xff08;每页的字节数&#xff09; 2^11 (寻址地址位数 11位)。 AT24C16有128(2^7128)页只需要7位地址&#xff0c;分为高3位和低4位&#xff0c;高3位在设备地址中&#xff0c;低4位在字地址中。 设备…