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

news2024/11/18 16:55:41

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

总结

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

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

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

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

相关文章

AI重塑视觉体验:将图像与视频转化为逼真可编辑的3D虚拟场景

在这个数字化飞速发展的时代,AI技术正以前所未有的方式重塑我们的视觉体验。特别是当AI能够轻松将普通的照片和视频转化为高度逼真、可交互的3D虚拟场景时,它不仅简化了3D内容创作的复杂性,还极大地拓宽了应用场景的边界。今天,我…

医学数据分析实训 项目二 数据预处理作业

文章目录 项目二 数据预处理一、实践目的二、实践平台三、实践内容任务一:合并数据集任务二:独热编码任务三:数据预处理任务四:针对“项目一 医学数据采集”中“3. 通过 UCI 机器学习库下载数据集”任务所下载的数据集进行预处理。…

如何判断硬盘是不是固态硬盘?介绍几种简单有效方法

随着科技的发展,固态硬盘(SSD)因其读写速度快、噪音小、抗震能力强等优点,逐渐取代了传统的机械硬盘(HDD)。然而,对于普通用户来说,如何判断自己的硬盘是否为固态硬盘可能是一个难题…

10分钟在网站上增加一个AI助手

只需 10 分钟,为您的网站添加一个 AI 助手,以便全天候(7x24)回应客户咨询,提升用户体验、增强业务竞争力。 方案概览 在网站中引入一个 AI 助手,只需 4 步: 创建大模型问答应用:我们…

prometheus监控k8s1.24以上版本pod实时数据指标

8s组件本身提供组件自身运行的监控指标以及容器相关的监控指标。通过cAdvisor 是一个开源的分析容器资源使用率和性能特性的代理工具,集成到 Kubelet中,当Kubelet启动时会同时启动cAdvisor,且一个cAdvisor只监控一个Node节点的信息。cAdvisor…

Transformer学习记录(6):Vision Transformer

背景 Transformer模型最初是使用在NLP中,但近几年Transformer模型在图像上的使用越来越频繁,最新的模型也出现了很多基于Transfomer的,而其中经典的是Vision Transformer(ViT),它是用于图像分类的,这里就以ViT-B/16这…

【已解决】SpringBoot3项目整合Druid依赖:Druid监控页面404报错

文章标题 问题描述原因分析解决方案参考资料 问题描述 最近,笔者在SpringBoot3项目中整合Druid连接池时,偶然翻到一条介绍Druid监控的短视频,兴致盎然之下尝试设置了一下Druid监控。 But,按照视频中提供的yml参数对照设置&#x…

试用过这么多ERP管理系统,还是这几款值得推荐!

已经考虑引入ERP系统却担心面临以下问题?ERP系统流程僵化难以调整?流程与实际业务脱节?培训不到位、技术支持不及时导致难以实现全员使用、共创?市面上的ERP系统众多不知道如何选择? 今天就根据2024最新市场动态&…

C++开发基础之理解 CUDA 编译配置:`compute_XX` 和 `sm_XX` 的作用

前言 在 CUDA 编程中,确保代码能够在不同的 NVIDIA GPU 上高效运行是非常重要的。为了实现这一点,CUDA 编译器 (nvcc) 提供了多种配置选项,其中 compute_XX 和 sm_XX 是两个关键的编译选项。本文将深入探讨这两个选项的作用及其配置顺序&…

什么是VHDX文件?

VHDX文件是Docker环境中使用的一种虚拟硬盘文件格式,专门用于存储Docker容器 的镜像和数据。这种文件格式,VHDX,代表Virtual Hard Disk Extended,是一种用于虚拟化环境的硬盘文件格式,支持动态扩展、快照、加密等功能&…

53.【C语言】 字符函数和字符串函数(strcmp函数)

7.strcmp函数 *简单使用 cplusplus的介绍 点我跳转 strcmp:string compare 字符串比较 具体讲解见此文 点我跳转 *例题 求下列代码的执行结果 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> int main() {char arr1[20] { &quo…

MoE 混合专家模型

MoE特点 做了特征增广&#xff1b;门控机制保证了特征增广的同时模型不会过拟合。 大模型中会使用到MoE技术。 简单实现 实现一个简单的linear混合专家模型。这里每个专家是一个Linear。 MoE简单理解 import torch import torch.nn as nn import torch.nn.functional as F#…

军用软件安全性可靠性设计与编码规范技术培训

​课程介绍&#xff1a; 随着我国武器装备数字化、智能化水平不断提高&#xff0c;软件在武器装备中的作用越来越关键。由于软件能以人所不能比的速度可靠地完成关键控制任务&#xff0c;几乎在每个重要系统中都由计算机系统来进行控制&#xff0c;软件在运行和控制危险以及在…

一个软件分发和下载的网站源码,带多套模板

PHP游戏应用市场APP软件下载平台网站源码手机版 可自行打包APP&#xff0c;带下载统计&#xff0c;带多套模板&#xff0c;带图文教程 代码下载&#xff1a;百度网盘

Unity生命周期_一些容易忽略的点>重复的生命周期代码会执行子类的。

图 官方的图&#xff1a;https://docs.unity.cn/cn/2019.4/Manual/ExecutionOrder.html 忽略点 父类和子类有相同的生命周期 问题描述&#xff1a; 当父类和子类都写了Awake &#xff0c;会执行子类的Awake &#xff0c;而不会执行父类的Awake using UnityEngine;public …

【Ubuntu】Ubuntu双网卡配置 实现内外网互不影响同时可用

【Ubuntu】Ubuntu双网卡配置 实现内外网互不影响同时可用 建议前提配置用到的命令参考文献&#xff1a; 建议 本文仅作个人记录&#xff0c;请勿完全照搬&#xff0c;建议直接看此视频&#xff0c;按作者的步骤进行配置 linux配置内外网&#xff08;ubuntu举例&#xff09;&am…

c++面试-语法糖(一)

c面试-语法糖(一) 1、const关键字的作用&#xff1f;(变量&#xff0c;参数&#xff0c;返回值) 定义常量值&#xff1a;const 可以用于定义常量变量&#xff0c;其值在初始化后不能被修改。 const int MAX_SIZE 100;修饰指针&#xff1a;const 可以修饰指针&#xff0c;表示…

第158天:安全开发-Python-Socket编程反弹Shell分离免杀端口探针域名爆破

前置知识 使用 socket 模块 1. 导入模块 首先&#xff0c;你需要导入 Python 的 socket 模块。 import socket 2. 创建套接字 使用 socket.socket() 函数创建一个新的套接字。这个函数可以接收两个参数&#xff1a;地址族和套接字类型。 地址族&#xff08;Address Family&…

【鸿蒙】HarmonyOS NEXT星河入门到实战9-组件化开发进阶应用状态管理

目录 1.1 创建页面 1.2 页面跳转和后退 1.3 页面栈 1.4 路由模式 1.5 路由传参 2、生命周期 3、Stage模型 3.1 目录概览 3.2 app.json5应用配置 3.3 module.json5模型配置 3.4 UIAbility组件 3.5 UIAbility的添加和设置启动 3.6 UIAbility组件的生命周期 3.7 拉起另…

LSS如何创建视锥

先创建图像(u,v,d)坐标系下视锥 再根据相机内外参,将图像坐标系下uvd视锥x6,再调整到世界坐标系下XYZ视锥 1 完整代码 de