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

🎯 设计模式专栏,持续更新中

欢迎订阅:JAVA实现设计模式

🛠️ 希望小伙伴们一键三连,有问题私信都会回复,或者在评论区直接发言

外观模式

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

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

实际案例:家庭影院系统

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

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

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

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

组件

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

代码实现

Step 1: 创建子系统类

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

java 复制代码
// 子系统类 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,它封装了对各个子系统的调用,提供简化的接口。

java 复制代码
// 外观类: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 来控制整个家庭影院系统。

java 复制代码
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();
    }
}

输出结果

java 复制代码
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 提供的方法即可操作对象。

java 复制代码
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 提供了统一的接口,封装了多个子系统的操作,使得代码更加简洁和可维护。

总结

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

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

相关推荐
MapleLea1f11 分钟前
26届JAVA 学习日记——Day14
java·开发语言·学习·tcp/ip·程序人生·学习方法
没有黑科技16 分钟前
基于web的音乐网站(Java+SpringBoot+Mysql)
java·前端·spring boot
计算机毕设孵化场17 分钟前
计算机毕设-基于springboot的多彩吉安红色旅游网站的设计与实现(附源码+lw+ppt+开题报告)
vue.js·spring boot·后端·计算机外设·课程设计·计算机毕设论文·多彩吉安红色旅游网站
爪哇学长18 分钟前
解锁API的无限潜力:RESTful、SOAP、GraphQL和Webhooks的应用前景
java·开发语言·后端·restful·graphql
刘大浪31 分钟前
IDEA 2024安装指南(含安装包以及使用说明 cannot collect jvm options 问题 四)
java
Cod_Next36 分钟前
Mac系统下配置 Tomcat 运行环境
java·macos·tomcat
小白不太白95041 分钟前
设计模式之建造者模式
java·设计模式·建造者模式
p-knowledge42 分钟前
建造者模式(Builder Pattern)
java·开发语言·建造者模式
战神刘玉栋42 分钟前
《SpringBoot、Vue 组装exe与套壳保姆级教学》
vue.js·spring boot·后端
Str_Null1 小时前
Seatunnel运行时报错Caused by: java.lang.NoClassDefFoundError: com/mysql/cj/MysqlType
java·seatunnel