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

🎯 设计模式专栏,持续更新中
欢迎订阅: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 文档 需掌…

如何利用 Redis 列表实现一个简单的任务调度系统?

以下是利用 Redis 列表实现一个简单任务调度系统的步骤: 一、任务存储 定义任务格式 每个任务可以是一个包含任务相关信息的字符串或者 JSON 对象。例如,任务可以包含任务 ID、任务执行的函数名、执行参数、执行时间等信息。例如,一个简单的任务可以表示为{"id":…

【运维监控】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) 成…

JavaScript知识点3

目录 1.JavaScript中有多少个线程? 2.如何判断一个对象是不是空对象? 3.什么事JavaScript时间死区? 4.什么是JSON stringify? 1.JavaScript中有多少个线程? JavaScript中的主线程负责执行代码、处理事件和更新用户…

最大余额法,解决百分比计算相加不等于100%(扇形/饼图百分比使用的此算法)

在开发项目的过程中有时候需要进行计算百分比,例如计算饼状图百分比。有时候在计算的过程中常规四舍五入计算会发生所有计算的值相加不等于100%的情况 这是 get_percent_value 函数的 JavaScript 版本: /*** 最大余额法,解决百分比计算相加不…

什么是蜘蛛池?有什么作用

在网络爬虫的世界里,蜘蛛池(Spider Pool)是一个专门用于管理和维护大量爬虫的系统。它为爬虫提供了一个集中的工作环境,使得爬虫能够更高效、更稳定地进行数据抓取。本文将探讨蜘蛛池的概念、组成以及它在现代网络爬虫技术中的作用…

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

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

llvm后端之函数栈帧

llvm后端之函数栈帧 引言1 目标扩展实现1.1 emitPrologue和emitEpilogue1.2 storeRegToStackSlot和loadRegFromStackSlot 2 寄存器存栈与恢复 引言 llvm后端在物理寄存器分配后、指令发射前会调用PEI这个pass来生成函数栈帧的创建与销毁。 1 目标扩展实现 在target下&#x…

使用PyTorch进行图像风格迁移:基于VGG19实现

图像风格迁移&#xff08;Neural Style Transfer, NST&#xff09;是深度学习中一个令人着迷的应用&#xff0c;它能够将一张图像的风格应用到另一张图像上。例如&#xff0c;能够将梵高的画风应用到一张普通照片上。本文将详细解释如何使用PyTorch进行风格迁移&#xff0c;逐步…

解决el-table排序sortable只排序当前页问题

el-table-column中添加sortable只是让每页数据单独排序&#xff0c;没有把所有数据进行排序&#xff0c;可以通过sort-change事件解决。 1、首先在需要排序的列上加sortable“custom” <el-table-columnprop"date"label"日期"width"180"sort…

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;通常是作为划分编…

Flume:大规模日志收集与数据传输的利器

Flume&#xff1a;大规模日志收集与数据传输的利器 在大数据时代&#xff0c;随着各类应用的不断增长&#xff0c;产生了海量的日志和数据。这些数据不仅对业务的健康监控至关重要&#xff0c;还可以通过深入分析&#xff0c;帮助企业做出更好的决策。那么&#xff0c;如何高效…

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

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

Oracle EBS中AR模块的财务流程概览

应收账款 (AR) 模块是Oracle E-Business Suite (EBS) 中另一个重要的财务管理模块&#xff0c;主要用于管理企业销售过程中的账款回收。下面是AR模块中的一些关键财务流程及其详细说明&#xff1a; 1. 销售订单管理 创建销售订单&#xff1a;当客户下单时&#xff0c;销售人员…