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

外观设计模式

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

概述

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

定义:

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

​ 外观(Facade)模式是"迪米特法则 "的典型应用

结构

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

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

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

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

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

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

具体案例:

案例一:

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

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

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

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

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

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

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

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

案例二:

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

子系统类:

java 复制代码
/**
 * @author OldGj 2024/02/27
 * @version v1.0
 * @apiNote 子系统 - 空调类
 */
public class AirCondition {
    public void on(){
        System.out.println("打开空调...");
    }

    public void off(){
        System.out.println("关闭空调...");
    }
}
java 复制代码
/**
 * @author OldGj 2024/02/27
 * @version v1.0
 * @apiNote 子系统角色 - 电灯类
 */
public class Light {

    public void on(){
        System.out.println("打开电灯...");
    }

    public void off(){
        System.out.println("关闭电灯...");
    }
}
java 复制代码
/**
 * @author OldGj 2024/02/27
 * @version v1.0
 * @apiNote 子系统 - 电视类
 */
public class TV {

    public void on(){
        System.out.println("打开电视...");
    }

    public void off(){
        System.out.println("关闭电视...");
    }
}

外观类:

java 复制代码
/**
 * @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. 提高可维护性:外观将系统的实现细节封装起来,使得系统更容易维护和修改。

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

  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类和流操作。

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

相关推荐
马剑威(威哥爱编程)37 分钟前
读写锁分离设计模式详解
java·设计模式·java-ee
修道-032339 分钟前
【JAVA】二、设计模式之策略模式
java·设计模式·策略模式
G皮T4 小时前
【设计模式】结构型模式(四):组合模式、享元模式
java·设计模式·组合模式·享元模式·composite·flyweight
W_Meng_H4 小时前
设计模式-组合模式
设计模式·组合模式
吾与谁归in14 小时前
【C#设计模式(8)——过滤器模式(Adapter Pattern)】
设计模式·c#·过滤器模式
G皮T15 小时前
【设计模式】行为型模式(一):模板方法模式、观察者模式
java·观察者模式·设计模式·模板方法模式·template method·行为型模式·observer
password大鸭梨17 小时前
一个简单ASP.NET购物车设计
windows·microsoft·asp.net
iFlyCai18 小时前
23种设计模式的Flutter实现第一篇创建型模式(一)
flutter·设计模式·dart
zhouzhihao_0718 小时前
程序代码设计模式之模板方法模式(1)
java·设计模式·模板方法模式
xianwu54318 小时前
【设计模式】工厂模式
开发语言·c++·设计模式·简单工厂模式·抽象工厂模式