• 【深入理解设计模式】外观设计模式


    外观设计模式

    在这里插入图片描述

    外观设计模式(Facade Design Pattern)是一种结构型设计模式,旨在为复杂系统提供简单的接口。该模式通过为子系统提供一个高级接口,使得客户端与子系统之间的交互更加简单。外观设计模式通常被用来隐藏系统的复杂性,并且提供一个简化的接口,以便客户端能够更容易地使用系统。

    概述

    有些人可能炒过股票,但其实大部分人都不太懂,这种没有足够了解证券知识的情况下做股票是很容易亏钱的,刚开始炒股肯定都会想,如果有个懂行的帮帮手就好,其实基金就是个好帮手,支付宝里就有许多的基金,它将投资者分散的资金集中起来,交由专业的经理人进行管理,投资于股票、债券、外汇等领域,而基金投资的收益归持有者所有,管理机构收取一定比例的托管管理费用。

    定义:

    外观模式又名门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体的细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。

    ​ 外观(Facade)模式是“迪米特法则”的典型应用
    在这里插入图片描述

    结构

    以下是外观设计模式的主要组成部分和工作原理:

    1. 外观(Facade):外观是客户端与系统之间的接口,它向客户端提供了一个简单的接口,隐藏了系统的复杂性。外观通常是一个单一的类或接口。

    2. 子系统(Subsystems):子系统是系统中的各个模块或组件,它们实现了系统的各个功能。这些子系统可能是相互独立的,但它们在外观的统一接口下协同工作。

    3. 客户端(Client):客户端是使用外观模式的应用程序的组成部分。它通过外观接口与系统进行交互,而不需要了解系统的内部实现细节。

    外观设计模式的工作原理如下:

    • 客户端通过外观接口与系统交互。
    • 外观将客户端的请求委派给相应的子系统。
    • 子系统执行请求,并将结果返回给外观。
    • 外观将结果返回给客户端。

    具体案例:

    案例一:

    让我们以一个简单的家庭影院系统为例,来演示外观设计模式的使用。假设这个家庭影院系统包括DVD播放器投影仪音响系统

    首先,我们定义子系统的类:

    // DVD播放器
    class DVDPlayer {
        public void on() {
            System.out.println("DVD Player is on");
        }
        
        public void play(String movie) {
            System.out.println("Playing movie: " + movie);
        }
        
        public void off() {
            System.out.println("DVD Player is off");
        }
    }
    
    // 投影仪
    class Projector {
        public void on() {
            System.out.println("Projector is on");
        }
        
        public void setInput(DVDPlayer dvdPlayer) {
            System.out.println("Setting DVD Player input to Projector");
        }
        
        public void off() {
            System.out.println("Projector is off");
        }
    }
    
    // 音响系统
    class SoundSystem {
        public void on() {
            System.out.println("Sound System is on");
        }
        
        public void setVolume(int volume) {
            System.out.println("Setting volume to " + volume);
        }
        
        public void off() {
            System.out.println("Sound System is off");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    然后,我们定义外观类来封装这些子系统,提供一个简单的接口给客户端:

    // 外观类
    class HomeTheaterFacade {
        private DVDPlayer dvdPlayer;
        private Projector projector;
        private SoundSystem soundSystem;
        
        public HomeTheaterFacade(DVDPlayer dvdPlayer, Projector projector, SoundSystem soundSystem) {
            this.dvdPlayer = dvdPlayer;
            this.projector = projector;
            this.soundSystem = soundSystem;
        }
        
        public void watchMovie(String movie, int volume) {
            dvdPlayer.on();
            projector.on();
            projector.setInput(dvdPlayer);
            soundSystem.on();
            soundSystem.setVolume(volume);
            dvdPlayer.play(movie);
        }
        
        public void endMovie() {
            dvdPlayer.off();
            projector.off();
            soundSystem.off();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    最后,我们使用客户端来调用外观接口,而不需要了解子系统的复杂性:

    public class Client{
        public static void main(String[] args) {
            // 创建子系统对象
            DVDPlayer dvdPlayer = new DVDPlayer();
            Projector projector = new Projector();
            SoundSystem soundSystem = new SoundSystem();
            
            // 创建外观对象
            HomeTheaterFacade homeTheater = new HomeTheaterFacade(dvdPlayer, projector, soundSystem);
            
            // 调用外观接口观看电影
            homeTheater.watchMovie("Avatar", 10);
            
            // 结束电影
            homeTheater.endMovie();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    这样,客户端只需调用外观的 watchMovie 方法来观看电影,而无需直接与DVD播放器、投影仪和音响系统进行交互。外观类 HomeTheaterFacade 封装了系统的复杂性,提供了一个简单的接口给客户端使用。

    案例二:

    我们定义一个智能家电控制系统,小明的爷爷已经60岁了,一个人在家生活:每次都需要打开灯、打开电视、打开空调;睡觉时关闭灯、关闭电视、关闭空调;操作起来都比较麻烦。所以小明给爷爷买了智能音箱,可以通过语音直接控制这些智能家电的开启和关闭。

    子系统类:

    /**
     * @author OldGj 2024/02/27
     * @version v1.0
     * @apiNote 子系统 - 空调类
     */
    public class AirCondition {
        public void on(){
            System.out.println("打开空调...");
        }
    
        public void off(){
            System.out.println("关闭空调...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    /**
     * @author OldGj 2024/02/27
     * @version v1.0
     * @apiNote 子系统角色 - 电灯类
     */
    public class Light {
    
        public void on(){
            System.out.println("打开电灯...");
        }
    
        public void off(){
            System.out.println("关闭电灯...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    /**
     * @author OldGj 2024/02/27
     * @version v1.0
     * @apiNote 子系统 - 电视类
     */
    public class TV {
    
        public void on(){
            System.out.println("打开电视...");
        }
    
        public void off(){
            System.out.println("关闭电视...");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    外观类:

    /**
     * @author OldGj 2024/02/27
     * @version v1.0
     * @apiNote 外观角色 - 智能语言助手类
     */
    public class SmartAppliancesFacade {
    
        private final TV tv;
        private final Light light;
        private final AirCondition airCondition;
    
        public SmartAppliancesFacade() {
            tv = new TV();
            airCondition = new AirCondition();
            light = new Light();
        }
    
        public void say(String message) {
            if(message.contains("打开")) {
                on();
            } else if(message.contains("关闭")) {
                off();
            } else {
                System.out.println("我还听不懂你说的!!!");
            }
        }
        // 一键开启家电
        private void on(){
            tv.on();
            airCondition.on();
            light.on();
        }
    
        // 一键关闭家电
        private void off(){
            tv.off();
            airCondition.off();
            light.off();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    通过以上外观类对家电的封装,客户端只需要直接调用外观类的方法,即可操作子系统中的功能。

    外观设计模式的优点包括:

    1. 简化接口:外观提供了一个简单的接口,隐藏了系统的复杂性,使得客户端更容易使用系统。
    2. 解耦:外观将客户端与子系统之间解耦,使得子系统的变化不会影响到客户端
    3. 提高可维护性:外观将系统的实现细节封装起来,使得系统更容易维护和修改。

    然而,外观设计模式也有一些缺点:

    1. 限制灵活性:外观定义了一个固定的接口,可能会限制系统的灵活性。
    2. 增加了类和接口的数量:引入外观可能会增加系统中的类和接口的数量,使得系统更加复杂。
    3. 不符合开闭原则,修改很麻烦

    使用场景

    外观设计模式通常在以下情况下使用:

    1. 简化复杂系统:当系统包含多个复杂的子系统,并且客户端需要与这些子系统进行交互时,可以使用外观设计模式来简化客户端的操作。外观类封装了子系统的复杂性,提供了一个简单的接口给客户端。

    2. 隐藏实现细节:当客户端不需要了解系统的内部实现细节,只需要一个简单的接口来与系统交互时,可以使用外观设计模式。外观类隐藏了系统的实现细节,使得客户端不需要直接与子系统进行交互。

    3. 解耦客户端和子系统:当客户端与多个子系统之间存在耦合关系时,可以使用外观设计模式来解耦客户端和子系统。外观类充当了客户端与子系统之间的中介,使得客户端与子系统之间的耦合度降低。

    4. 提供简化的接口:当系统的接口过于复杂或冗长时,可以使用外观设计模式来提供一个简化的接口给客户端使用。外观类将系统的复杂性隐藏起来,提供了一个简单的接口给客户端。

    5. 封装变化:当系统中的子系统可能发生变化时,可以使用外观设计模式来封装这些变化。客户端不需要知道系统内部的变化,只需要通过外观类来与系统交互。

    总的来说,外观设计模式适用于需要简化复杂系统接口、隐藏实现细节、解耦客户端和子系统以及提供简化接口等情况。通过使用外观设计模式,可以提高系统的易用性和可维护性,同时降低客户端与子系统之间的耦合度。

    源码中的外观模式:

    在Tomcat中,RequestFacade对象使用了外观设计模式。在Servlet容器中,RequestFacade是一个包装类,它封装了HttpServletRequest对象,并提供了简化的接口给Servlet类使用。RequestFacade隐藏了HttpServletRequest对象的复杂性,使得Servlet类不需要直接与HttpServletRequest对象进行交互,而是通过RequestFacade对象来访问请求的信息。

    通过使用外观设计模式,RequestFacade对象提供了一个简化的接口给Servlet类使用,同时封装了HttpServletRequest对象的实现细节,使得Servlet类不需要关心HttpServletRequest对象的具体实现。这样,RequestFacade对象简化了Servlet类的开发,提高了系统的可维护性和可扩展性。

    在现实的源代码中,外观模式可能存在于各种形式中,具体取决于应用程序的结构和需求。以下是一些常见的源码示例,展示了外观模式在不同上下文中的应用:

    1. Java类库中的网络请求发送
      在Java类库中,像Apache HttpClient或者HttpURLConnection等网络请求库的使用中,可能会使用外观模式。这些库封装了底层的网络请求细节,提供了一个简单的接口给开发者来发送HTTP请求,隐藏了网络请求的复杂性。

    2. Spring框架中的JdbcTemplate
      在Spring框架中,JdbcTemplate是一个用于简化JDBC编程的类,它封装了底层的JDBC细节,提供了一组简单的方法来执行SQL查询和更新操作。开发者可以通过JdbcTemplate来与数据库进行交互,而不需要直接使用JDBC API。

    3. Android开发中的ContentResolver
      在Android开发中,ContentResolver是用于访问ContentProvider提供的数据的类。ContentResolver封装了底层的ContentProvider细节,提供了一组简单的方法来查询、插入、更新和删除数据,隐藏了ContentProvider的复杂性。

    4. 日志记录框架中的Logger
      在日志记录框架(如Log4j、Logback等)中,Logger封装了底层的日志记录细节,提供了一组简单的方法来记录日志信息。开发者可以通过Logger来记录日志,而不需要直接操作日志记录器、格式化器等组件。

    5. 文件操作库中的FileUtils
      在文件操作库(如Apache Commons IO)中,FileUtils封装了底层的文件操作细节,提供了一组简单的方法来操作文件和目录。开发者可以通过FileUtils来复制、移动、删除文件等,而不需要直接使用File类和流操作。

    在这些情况下,外观模式封装了底层的复杂性,提供了一个简单的接口给开发者使用,从而简化了开发过程,提高了代码的可维护性和可读性。

  • 相关阅读:
    【Python】Python 实现 Excel 到 CSV 的转换程序
    Servlet
    探索未来的AI革命:GPT-5的即将登场
    猿创征文|瑞吉外卖——管理端_菜品管理_1
    手写Spring——bean的扫描、加载和实例化
    [附源码]java毕业设计游戏战队考核系统
    了解OpenGL的Program Pipeline:
    放下宝宝就醒,告诉你是因为什么
    java面试题
    【Kubernetes系列】Kubernetes管理工具Kuboard的安装使用
  • 原文地址:https://blog.csdn.net/guojiaqi_/article/details/136421977