外观模式详解:如何为复杂系统构建简洁的接口

🎯 设计模式专栏,持续更新中
欢迎订阅:JAVA实现设计模式
🛠️ 希望小伙伴们一键三连,有问题私信都会回复,或者在评论区直接发言

外观模式

外观模式(Facade Pattern)为子系统中的一组接口提供一个统一的接口。它是一个结构型设计模式,能够为复杂子系统提供一个更简单的接口,使客户端无需了解子系统的内部细节。

简而言之,外观模式通过创建一个外观类,简化对子系统的访问,隐藏系统的复杂性。

实际案例:家庭影院系统

想象你有一个家庭影院系统,包括以下多个组件:

  • 电视
  • 音响系统
  • 蓝光播放器
  • 灯光
  • 投影仪

要启动这个家庭影院,你通常需要打开多个设备,设置音响,调节灯光等等。显然,这个过程非常繁琐。为了简化操作,可以使用一个“外观类”,通过一个简单的方法来一次性处理所有这些操作。

类图解释:外观模式 UML 类图

在这里插入图片描述

组件

  1. Facade(外观类):为子系统提供一个统一的接口。
  2. Subsystem(子系统类):一组复杂的子系统类,每个子系统都有各自的复杂功能。
  3. Client(客户端):通过外观类与子系统进行交互,不直接调用子系统的功能。

代码实现

Step 1: 创建子系统类

首先定义家庭影院的子系统,包括 DVD 播放器、音响系统和投影仪。

// 子系统类 1:DVDPlayer
public class DVDPlayer {
    public void on() {
        System.out.println("DVD Player is on.");
    }

    public void playMovie() {
        System.out.println("DVD Player is playing the movie.");
    }

    public void off() {
        System.out.println("DVD Player is off.");
    }
}

// 子系统类 2:SoundSystem
public class SoundSystem {
    public void on() {
        System.out.println("Sound System is on.");
    }

    public void setVolume(int level) {
        System.out.println("Sound System volume set to " + level);
    }

    public void off() {
        System.out.println("Sound System is off.");
    }
}

// 子系统类 3:Projector
public class Projector {
    public void on() {
        System.out.println("Projector is on.");
    }

    public void adjustScreen() {
        System.out.println("Projector screen adjusted.");
    }

    public void off() {
        System.out.println("Projector is off.");
    }
}

Step 2: 创建外观类

定义外观类 HomeTheaterFacade,它封装了对各个子系统的调用,提供简化的接口。

// 外观类:HomeTheaterFacade
public class HomeTheaterFacade {
    private DVDPlayer dvdPlayer;
    private SoundSystem soundSystem;
    private Projector projector;

    public HomeTheaterFacade(DVDPlayer dvdPlayer, SoundSystem soundSystem, Projector projector) {
        this.dvdPlayer = dvdPlayer;
        this.soundSystem = soundSystem;
        this.projector = projector;
    }

    // 启动家庭影院
    public void startMovie() {
        System.out.println("Starting the home theater...");
        dvdPlayer.on();
        soundSystem.on();
        soundSystem.setVolume(10);
        projector.on();
        projector.adjustScreen();
        dvdPlayer.playMovie();
    }

    // 关闭家庭影院
    public void endMovie() {
        System.out.println("Shutting down the home theater...");
        dvdPlayer.off();
        soundSystem.off();
        projector.off();
    }
}

Step 3: 客户端调用外观类

客户端通过外观类 HomeTheaterFacade 来控制整个家庭影院系统。

public class Client {
    public static void main(String[] args) {
        // 创建子系统对象
        DVDPlayer dvdPlayer = new DVDPlayer();
        SoundSystem soundSystem = new SoundSystem();
        Projector projector = new Projector();

        // 创建外观对象
        HomeTheaterFacade homeTheater = new HomeTheaterFacade(dvdPlayer, soundSystem, projector);

        // 启动家庭影院
        homeTheater.startMovie();

        // 结束家庭影院
        homeTheater.endMovie();
    }
}

输出结果

Starting the home theater...
DVD Player is on.
Sound System is on.
Sound System volume set to 10
Projector is on.
Projector screen adjusted.
DVD Player is playing the movie.
Shutting down the home theater...
DVD Player is off.
Sound System is off.
Projector is off.

外观类HomeTheaterFacade 封装了多个子系统的操作,通过提供简化的接口 startMovie()endMovie(),客户端不再需要关心各个子系统的细节。

子系统DVDPlayerSoundSystemProjector 是复杂的子系统,每个子系统都有自己的操作,但通过外观类,客户端可以轻松地控制整个家庭影院。

外观模式在 MyBatis 应用的源码分析

在 MyBatis 中,MetaObject 是一个帮助类,用于对目标对象进行操作,如获取或设置属性值、查找属性、检查属性是否可读或可写等。

  • 功能:封装了对对象属性的操作,提供了一个统一的接口,使得 MyBatis 不需要直接与底层反射机制打交道。

  • 外观类MetaObject

  • 子系统

    • ObjectWrapper:包装对象,用于处理对象的属性操作。
    • Reflector:缓存反射信息,简化反射调用。
    • PropertyTokenizer:解析属性表达式(如 user.name),用于处理多层次的对象属性。

    在这里插入图片描述

MetaObject 类的结构:

MetaObject 提供了一个简化的接口,用于封装多个复杂类的操作。它将 ObjectWrapperReflector 这些类的功能整合起来,外部用户无需直接和这些类打交道,只需通过 MetaObject 提供的方法即可操作对象。

public class MetaObject {
    private final Object originalObject;         // 原始对象
    private final ObjectWrapper objectWrapper;   // 对象包装器
    private final ReflectorFactory reflectorFactory;
    private final ObjectFactory objectFactory;
    private final WrapperFactory wrapperFactory;

    // 构造方法
    private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
        this.originalObject = object;
        this.objectFactory = objectFactory;
        this.wrapperFactory = objectWrapperFactory;
        this.reflectorFactory = reflectorFactory;
        this.objectWrapper = wrapperFactory.getWrapper(this, object);  // 包装原始对象
    }

    // 获取属性值
    public Object getValue(String name) {
        PropertyTokenizer prop = new PropertyTokenizer(name);
        if (prop.hasNext()) {
            MetaObject metaValue = metaObjectForProperty(prop.getName());
            if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
                return null;
            } else {
                return metaValue.getValue(prop.getChildren());
            }
        } else {
            return objectWrapper.get(prop);
        }
    }

    // 设置属性值
    public void setValue(String name, Object value) {
        PropertyTokenizer prop = new PropertyTokenizer(name);
        if (prop.hasNext()) {
            MetaObject metaValue = metaObjectForProperty(prop.getName());
            if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
                throw new ReflectionException("The property '" + prop.getName() + "' is null.");
            }
            metaValue.setValue(prop.getChildren(), value);
        } else {
            objectWrapper.set(prop, value);
        }
    }

    // 创建 MetaObject
    public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
        if (object == null) {
            return SystemMetaObject.NULL_META_OBJECT;
        } else {
            return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
        }
    }
}

MetaObject 如何简化操作

通过 MetaObject,用户不需要关心底层是如何通过反射、包装器、属性解析等机制操作对象的属性。用户只需要调用 getValue()setValue() 方法,MetaObject 会在内部处理这些复杂的逻辑。

操作流程:

  1. PropertyTokenizer 解析属性表达式:user.name 会被解析为 username,支持嵌套属性。
  2. ObjectWrapper 处理属性获取或设置:ObjectWrapper 封装了对目标对象属性的操作。
  3. Reflector 缓存反射信息:通过反射获取目标对象的属性信息,但为了提高性能,Reflector 缓存了反射的结果。

通过这些子系统类的配合,MetaObject 提供了一个统一的接口来操作对象的属性,隐藏了底层的复杂性。

MetaObject 使用外观模式的优点

简化对象操作:通过 MetaObject,用户可以轻松读取和修改对象的属性,而不需要关心底层的反射机制。

解耦复杂逻辑MetaObject 将属性解析、对象包装、反射操作等复杂逻辑封装起来,客户端代码不需要直接与这些复杂的子系统交互。

提高代码可维护性MetaObject 提供了统一的接口,封装了多个子系统的操作,使得代码更加简洁和可维护。

总结

外观模式通过引入一个简化的接口,将复杂的子系统隐藏在外观类之后,降低了系统的复杂性和耦合度。它在系统需要解耦客户端与多个子系统,或简化复杂操作时,非常有用。

通过上面的家庭影院案例,你可以看到外观模式如何通过外观类将多个复杂的子系统操作简化为统一的接口,提升代码的可读性和维护性。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

智能 Uber 发票 PDF 合并工具

在现代商务出行中,尤其是在跨国出差中,处理和整合大量 Uber 发票已成为一项不小的挑战。手动整理和合并这些发票不仅耗时,还容易出错。作为开发者,为什么不开发一个自动化工具,将这些任务交给代码来完成呢?…

成型的程序

加一个提示信息 加上python 常用的包 整个程序打包完 250M 安装 960MB matplot numpy pandas scapy pysearial 常用的包 (pyvisa)… … 啥都有 Python 解释器组件构建 要比 lua 容易的多 (C/Rust 的组件库)

钢材表面缺陷数据集以coco格式做好了数据集的划分,1200张训练集,600张验证集,对应的json文件也在里面

钢材表面缺陷数据集 以coco格式做好了数据集的划分,1200张训练集,600张验证集,对应的json文件也在里面。 钢材表面缺陷检测数据集营销介绍 项目背景: 钢材作为工业生产的重要原材料之一,其表面质量直接影响到成品的性…

MySQL之安装与基础知识

目录 一:在centos7上安装MySQL数据库 1.卸载默认存在的环境 2.配置mysql的yum源 3. 安装MySQL 4.登录mysql 5.设置MySQL的配置文件 二:MySQL基础知识 1.什么是数据库 2.主流数据库 3.服务器,数据库,表关系及使用案例 4…

预训练发展

预训练发展 1.ELMo2.GPT3.Bert3.1Ernie-baidu3.2Ernie- Tsinghua 4.GPT25.UNILM6.Transformer-XL & XLNet6.1方案一6.2方案三 7.Roberta8.SpanBert8.1SBO简介: 9.ALBERT9.1方案一9.2方案二9.3方案三 10.T511.GPT312.从"续写"到"回答"12.1SF…

基于51单片机的直流数字电流表proteus仿真

地址: https://pan.baidu.com/s/1adZbhgOBvvg0KsCO6_ZiAw 提取码:1234 仿真图: 芯片/模块的特点: AT89C52/AT89C51简介: AT89C52/AT89C51是一款经典的8位单片机,是意法半导体(STMicroelectro…

MySQL——数据类型(一)

目录 一、前言 二、数值类型 2.1 tinyint [unsigned] 2.1.1 插入合法数据 2.1.2 插入边界数据 2.1.3 插入不合法数据 2.1.4 结论 2.2 bit [n] 2.3 float [(m, d)] [unsigned] 2.3.1 float 特性 2.3.2 插入整数部分大于 m-d 的数字 2.3.3 插入小数部分大于 d 的数字…

【贪心算法】贪心算法

贪心算法简介 1.什么是贪心算法2.贪心算法的特点3.学习贪心的方向 点赞👍👍收藏🌟🌟关注💖💖 你的支持是对我最大的鼓励,我们一起努力吧!😃😃 1.什么是贪心算法 与其说是…

MYSQL数据库——MYSQL管理

MYSQL数据库安装完成后,自带四个数据库,具体作用如下: 常用工具 1.mysql 不是指mysql服务,而是指mysql的客户端工具 例如: 2.mysqladmin 这是一个执行管理操作的客户端程序,可以用它来检查服务器的配置和…

vs2022快捷键异常解决办法

安装了新版本的vs2022,安装成功后,发现快捷键发生异常,之前常用的快捷键要么发生改变,要么无法使用,比如原来注释代码的快捷键是ctrlec,最新安装版本变成了ctrlkc,以前编译代码的快捷键是F6或者…

算法入门-贪心1

第八部分:贪心 409.最长回文串(简单) 给定一个包含大写字母和小写字母的字符串 s ,返回通过这些字母构造成的最长的回文串 的长度。 在构造过程中,请注意 区分大小写 。比如 "Aa" 不能当做一个回文字符串…

记录小数点

记录data frame小数点后面省略掉0的问题 iloc得到的series .to_list() 0被省略掉 to_list() 可能会将浮点数转换为默认格式。先将数据转换为字符串以保留格式 df_2708.iloc[2,:].apply(lambda x: f{x:.3f}).to_list()自定义保留小数点后几位 def formatter(value):return &q…

自动驾驶自动泊车场景应用总结

自动泊车技术是当前智能驾驶技术的一个重要分支,其目标是通过车辆自身的感知、决策和控制系统,实现车辆在有限空间内的自主泊车操作。目前自动泊车可分为半自动泊车、全自动泊车、记忆泊车、自主代客泊车四种产品形态,其中, 根据搭载传感器和使用场景的不同,全自动泊车又可…

OpenGL笔记二十一之几何类设计

OpenGL笔记二十一之几何类设计 —— 2024-09-16 下午 bilibili赵新政老师的教程看后笔记 code review! 文章目录 OpenGL笔记二十一之几何类设计1.运行1.1.立方体运行1.2.球体运行 2.几何类搭建1.立方体分析2.球体分析3.图片资源文件4.关键实现4.1.geometry.h4.2.geometry.cpp…

vue3使用provide和inject传递异步请求数据子组件接收不到

前言 一般接口返回的格式是数组或对象,使用reactive定义共享变量 父组件传递 const data reactive([])// 使用settimout模拟接口返回 setTimeout(() > {// 将接口返回的数据赋值给变量Object.assign(data, [{ id: 10000 }]) }, 3000);provide(shareData, dat…

ip映射域名,一般用于mysql和redis的固定映射,方便快捷打包

举个例子 192.168.3.101mysql映射到mysql.smartlink.com 192.168.3.101redis redis.smartlink.com 要将IP地址映射到域名,可以通过几种方式实现,包括修改本地主机文件(仅适用于本地开发环境)、设置DNS解析(适用于生产环…

一文入门生成式AI(理解ChatGPT的原理)

一、什么是生成式AI? 以ChatGPT为代表的生成式AI,是对已有的数据和知识进行向量化的归纳,总结出数据的联合概率。从而在生成内容时,根据用户需求,结合关联字词的概率,生成新的内容。 可以这么联想&#x…

el-table表格的展开行,初始化的时候展开哪一行+设置点击行可展开功能

效果: 表格展开行官网使用: 通过设置 type"expand" 和 Scoped slot 可以开启展开行功能,el-table-column 的模板会被渲染成为展开行的内容,展开行可访问的属性与使用自定义列模板时的 Scoped slot 相同。 但是这种方法…

解决:Vue 中 debugger 不生效

目录 1,问题2,解决2.1,修改 webpack 配置2.2,修改浏览器设置 1,问题 在 Vue 项目中,可以使用 debugger 在浏览器中开启调试。但有时却不生效。 2,解决 2.1,修改 webpack 配置 通…

【农信网-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞…