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

🎯 设计模式专栏,持续更新中
欢迎订阅: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);}}// 创建 MetaObjectpublic 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 提供了统一的接口,封装了多个子系统的操作,使得代码更加简洁和可维护。

总结

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

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

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

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

相关文章

linux系统安装miniconda3

一、下载minconda3 下载地址:https://docs.conda.io/en/latest/miniconda.html 一般国内访问比较困难,可到清华软件镜像站 Index of /anaconda/miniconda/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 需要特别注意自己的下载版本和自己的…

初识爬虫2

requests学习: 小技巧,如果你用的也是pycharm,对于控制台输出页面因为数据很长一行,不方便进行查看, 可以让它自动换行: 1.requests文档阅读学习链接:快速上手 — Requests 2.18.1 文档 需掌…

【运维监控】Prometheus+grafana监控zookeeper运行情况

运维监控系列文章入口:【运维监控】系列文章汇总索引 文章目录 一、prometheus二、grafana三、prometheus集成grafana监控zookeeper1、修改zookeeper配置2、修改prometheus配置3、导入grafana模板4、验证 本示例通过zookeeper自带的监控信息暴露出来,然后…

基于imx6ull平台opencv的图像采集和显示屏LCD显示功能(带Qt界面)

目录 一、概述二、环境要求2.1 硬件环境2.2 软件环境三、开发流程3.1 编写测试3.2 验证功能一、概述 本文档是针对imx6ull平台opencv的图像采集和显示屏LCD显示功能,创建Qt工程,在工程里面通过点击按钮,实现opencv通过摄像头采集视频图像,将采集的视频图像送给显示屏LCD进…

LabVIEW编程快速提升的技术

在LabVIEW程序员的成长过程中,很多技术和概念看似简单、常用,但真正掌握并能熟练运用,往往需要踏踏实实的实践与积累。没有什么是能够一蹴而就的,唯有通过不断的专注与深入,才能获得显著的提升。要想在LabVIEW开发上取…

SSM框架学习(三、MyBatis实践:提高持久层数据处理效率)

目录 一、Mybatis简介 1.简介 2.持久层框架对比 3.快速入门(基于Mybatis3方式) 4.ibatis方式的实现和原理 5.ibatis与mybatis之间的关系 二、Mybatis基本使用 1.向 sql 语句传参 (1)mybatis日志输出配置 (2&…

为什么矩阵特征值之和等于主对角线元素之和,特征值乘积等于行列式值

首先给出特征值和特征向量的定义。 设A是n阶矩阵,如果数λ和n维非零向量x使关系式 Axλx (1) 成…

微信小程序使用canvas画图保存图片到手机相册

微信小程序要实现使用canvas绘制一个图&#xff0c;然后保存到手机相册 **最终效果&#xff1a;**实现生成以下图片 一、初始化canvas // wxml页面设置canvas标签 <canvas style"width: {{windowW}}px; height: {{windowH}}px;" disable-scrolltrue canvas-id&…

C++(2)之Linux多线程服务端编程总结

C之Linux多线程服务端编程读书笔记 Author: Once Day Date: 2023年1月31日/2024年8月23日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: Linux实践…

C++:类与对象

一、面向对象编程 (一) 面向过程vs面向对象 面向过程&#xff08;Procedural-Oriented-Programming&#xff0c; POP&#xff09;和面向对象&#xff08;Object-Oriented-Programming&#xff0c;OOP&#xff09;&#xff0c;是两种典型的编程范式&#xff0c;通常是作为划分编…

频带宽度固定,如何突破数据速率的瓶颈?

目录 目录 引言 信道 频带宽度 信噪比 信噪比的重要性 影响信噪比的因素 码元 码元的特点&#xff1a; 码元与比特的关系&#xff1a; 码元的作用&#xff1a; 码元的类型&#xff1a; Question 类比解释&#xff1a; 技术解释&#xff1a; 引言 在现代通信系统中…

OpenAI o1:AI领域的“草莓”革命,华人科学家贡献卓越

最近&#xff0c;科技界的热门明星“草莓”频繁出现在大家的视线中。9月11号&#xff0c;The Information报道称&#xff1a;OpenAI计划在未来两周内推出一款更智能、更昂贵、更谨慎的AI模型&#xff01;网友们对此消息持怀疑态度&#xff0c;认为类似消息屡见不鲜&#xff0c;…

centos8构建nginx1.27.1+BoringSSL+http3+lua+openresty

需要接入http3&#xff0c;索性最新的nginx在构建一波&#xff0c;趟一遍坑 准备工作 1.环境命令安装 yum install GeoIP -y yum install GeoIP-devel -y yum install libmaxminddb-devel -y yum install -y patch wget zlib zlib-devel lftp gcc gcc-c make openssl-devel p…

Pikachu靶场之csrf

CSRF 跨站请求伪造 CSRF入门及靶场实战 - FreeBuf网络安全行业门户 攻击者伪造恶意链接&#xff0c;诱使用户点击&#xff0c;这个链接附带了用户的认证凭据Cookie、Session等&#xff0c;执行操作如转账。 因为带了cookie、session&#xff0c;服务器认为是用户的行为。借用…

待机模式中WKUP上升沿模拟开机与关机

本篇博客重点在于标准库函数的理解与使用&#xff0c;搭建一个框架便于快速开发 目录 前言 待机模式 代码 wkup.h wkup.c main.c 使用注意 前言 建议先阅读下面的博客中待机模式部分。本博客主要分享代码-基于待机模式WKUP引脚的上升沿实现类似长按开机与关机的功能…

二维码的原理以及Java生成二维码【中间带图片】

一、什么是二维码&#xff1a; 二维码 &#xff08;2-dimensional bar code&#xff09;&#xff0c;是用某种特定的几何图形按一定规律在平面&#xff08;二维方向上&#xff09; 分布的黑白相间的图形记录数据符号信息的。 二、常用的码制 Data Matrix, Maxi Code, Aztec,…

看看智慧门诊银医通自助服务方案,如何化解医院患者跑难题

“看病三分钟&#xff0c;排队三小时”&#xff0c;这是许多患者在就医过程中的无奈吐槽。挂号队伍长如龙&#xff0c;看病流程繁琐复杂&#xff0c;缴费窗口人满为患&#xff0c;检查报告等待时间漫长…… 这些就医痛点&#xff0c;不仅让患者身心疲惫&#xff0c;也给医院的管…

基于微信小程序+Java+SSM+Vue+MySQL的宿舍管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 基于微信小程序JavaSSMVueMySQL的宿舍管理系统【附源码文档…

F1C100S/F1C200S的资料来源说明

文章目录 常用板子开源创客荔枝派榴莲派 我想说是的官网啥资料都没有。但是它的资料又很多&#xff0c;从淘宝或者其他地方能都搜到很多。 http://wiki.lcmaker.com/index.php?titleLC-PI-200S https://github.com/peng-zhihui/Planck-Pi?tabreadme-ov-file#head4 http://do…

使用 PyCharm 新建 Python 项目详解

使用 PyCharm 新建 Python 项目详解 文章目录 使用 PyCharm 新建 Python 项目详解一 新建 Python 项目二 配置环境1 项目存放目录2 Python Interpreter 选择3 创建隔离环境4 选择你的 Python 版本5 选择 Conda executable 三 New Window 打开项目四 目录结构五 程序编写运行六 …