您现在的位置是:首页 >学无止境 >设计模式(十):结构型之外观模式网站首页学无止境

设计模式(十):结构型之外观模式

冬天vs不冷 2024-09-18 12:01:06
简介设计模式(十):结构型之外观模式

设计模式系列文章

设计模式(一):创建型之单例模式

设计模式(二、三):创建型之工厂方法和抽象工厂模式

设计模式(四):创建型之原型模式

设计模式(五):创建型之建造者模式

设计模式(六):结构型之代理模式

设计模式(七):结构型之适配器模式

设计模式(八):结构型之装饰器模式

设计模式(九):结构型之桥接模式

设计模式(十):结构型之外观模式



一、设计模式分类

  • 创建型模式
    • 用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”
    • 提供了单例、原型、工厂方法、抽象工厂、建造者 5 种创建型模式
  • 结构型模式
    • 用于描述如何将类或对象按某种布局组成更大的结构
    • 提供了代理、适配器、桥接、装饰、外观、享元、组合 7 种结构型模式
  • 行为型模式
    • 用于描述类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务,以及怎样分配职责
    • 提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器 11 种行为型模式

二、外观模式

1、概述

定义

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

在这里插入图片描述

2、结构

外观(Facade)模式包含以下主要角色:

  • 外观(Facade)角色:为多个子系统对外提供一个共同的接口
  • 子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它

3、实现

智能家电控制

  • 一个人在家生活:每次都需要打开灯、打开电视、打开空调
  • 睡觉时关闭灯、关闭电视、关闭空调;操作起来都比较麻烦
  • 买了智能音箱,可以通过语音直接控制这些智能家电的开启和关闭

类图如下:

在这里插入图片描述

代码如下:

  • 家电类
//灯类
public class Light {
    public void on() {
        System.out.println("打开了灯....");
    }

    public void off() {
        System.out.println("关闭了灯....");
    }
}

//电视类
public class TV {
    public void on() {
        System.out.println("打开了电视....");
    }

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

//控制类
public class AirCondition {
    public void on() {
        System.out.println("打开了空调....");
    }

    public void off() {
        System.out.println("关闭了空调....");
    }
}
  • 外观类,用户主要和该类对象进行交互
// 智能音箱
public class SmartAppliancesFacade {
    //聚合电灯对象,电视机对象,空调对象
    private Light light;
    private TV tv;
    private AirCondition airCondition;

    public SmartAppliancesFacade() {
        light = new Light();
        tv = new TV();
        airCondition = new AirCondition();
    }

    //通过语言控制
    public void say(String message) {
        if(message.contains("打开")) {
            on();
        } else if(message.contains("关闭")) {
            off();
        } else {
            System.out.println("我还听不懂你说的!!!");
        }
    }

    //一键打开功能
    private void on() {
        light.on();
        tv.on();
        airCondition.on();
    }

    //一键关闭功能
    private void off() {
        light.off();
        tv.off();
        airCondition.off();
    }
}
  • 客户端测试类
public class Client {
    public static void main(String[] args) {
        //创建外观对象
        SmartAppliancesFacade facade = new SmartAppliancesFacade();
        //客户端直接与外观对象进行交互
        facade.say("打开家电");
        
        System.out.println("==================");
        
        facade.say("关闭家电");
    }
}

结果:
打开电灯。。。。
打开电视机。。。。
打开空调。。。。
==================
关闭电灯。。。。
关闭电视机。。。。
关闭空调。。。。

好处

  • 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类
  • 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易

缺点

  • 不符合开闭原则,修改很麻烦

4、使用场景

  • 对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系
  • 当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问
  • 当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性

5、源码解析

  • 使用tomcat作为web容器时,接收浏览器发送过来的请求,tomcat会将请求信息封装成ServletRequest对象
  • ServletRequest是一个接口,它还有一个子接口HttpServletRequest
  • request对象肯定是一个HttpServletRequest对象的子实现类对象,到底是哪个类的对象呢?
  • 通过输出request对象,我们就会发现是一个名为RequestFacade的类的对象

在这里插入图片描述

  • RequestFacade类就使用了外观模式

结构图:

在这里插入图片描述

RequestFacade类:

public class RequestFacade implements HttpServletRequest {
    protected Request request = null;

    public RequestFacade(Request request) {
        this.request = request;
    }
	
	...

    public Object getAttribute(String name) {
        if (this.request == null) {
            throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
        } else {
            return this.request.getAttribute(name);
        }
    }

    public Enumeration<String> getAttributeNames() {
        if (this.request == null) {
            throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
        } else {
            return Globals.IS_SECURITY_ENABLED ? (Enumeration)AccessController.doPrivileged(new GetAttributePrivilegedAction()) : this.request.getAttributeNames();
        }
    }

    public String getCharacterEncoding() {
        if (this.request == null) {
            throw new IllegalStateException(sm.getString("requestFacade.nullRequest"));
        } else {
            return Globals.IS_SECURITY_ENABLED ? (String)AccessController.doPrivileged(new GetCharacterEncodingPrivilegedAction()) : this.request.getCharacterEncoding();
        }
    }
    ...
 }

为什么在此处使用外观模式呢?

  • 定义 RequestFacade 类,分别实现 ServletRequest
  • 同时定义私有成员变量 Request ,并且方法的实现调用 Request 的实现
  • 将 RequestFacade上转为 ServletRequest 传给 servlet 的 service 方法
  • 这样即使在 servlet 中被下转为 RequestFacade ,也不能访问私有成员变量对象中的方法
  • 既用了 Request ,又能防止其中方法被不合理的访问
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。