「全网最细 + 实战源码案例」设计模式——外观模式

概念

  • 外观模式是一种结构型模式,为复杂的子系统提供一个统一的接口 ,使得子系统的功能对外界更加简单、易用。

与真实世界的类比

  • 当你通过打电话给商店下达订单时,接线员就是该商店所有服务和部门的外观。 接线员为你提供了一个包含购物系统、支付网关、送货等服务的简单语言接口。

外观模式结构图

复制代码
// 空调
public class AirCondition {
    public void on() {
        System.out.println("AirCondition is on");
    }

    public void off() {
        System.out.println("AirCondition is off");
    }
}

// 灯
public class Light {

    public void on() {
        System.out.println("Light is on");
    }

    public void off() {
        System.out.println("Light is off");
    }
}

// 电视
public class TV {

    public void on() {
        System.out.println("TV on");
    }

    public void off() {
        System.out.println("TV off");
    }
}

// 外观类,封装了子系统的功能
public class SmartAppliancesFacade {
    private TV tv;
    private AirCondition airCondition;
    private Light light;
    public SmartAppliancesFacade() {
        tv = new TV();
        airCondition = new AirCondition();
        light = new Light();
    }

    public void say(String message){
        if(message.contains("打开")){
            allOn();
        }else if(message.contains("关闭")){
            allOff();
        }else{
            System.out.println("没有这个指令");
        }
    }

    public void allOn() {
        tv.on();
        airCondition.on();
        light.on();
    }

    public void allOff() {
        tv.off();
        airCondition.off();
        light.off();
    }

}

// 客户端类
public class Client {
    public static void main(String[] args) {
        SmartAppliancesFacade facade = new SmartAppliancesFacade();
        facade.say("打开家电");
        System.out.println("-----------------------------------------");

        facade.say("关闭家电");
        System.out.println("-----------------------------------------");

        facade.say("shfsahf");
        System.out.println("-----------------------------------------");
    }
}

适用的应用场景

  • 简化复杂系统:
    • 如果一个系统由多个模块组成,对外提供多个复杂接口,可以通过外观模式提供一个简单的接口调用。
    • 例如:支付系统涉及签名、网络请求、结果处理,通过 Facade Pattern 封装为一个简单的支付接口。
  • 分层架构设计:
    • 在分层系统种,可以在子系统的每一层使用 Facade Pattern ,减少高层模块对低层模块的依赖。
  • 对遗留系统的封装:
    • 当需要对已有系统添加新的功能或优化时,但不想破坏原有设计,可以通过 Facade Pattern 封装已有系统,提供新的接口。

优点

  1. 降低客户端与子系统的耦合:
    1. Client 无需了解 Subsystem 的具体实现。
  1. 提高子系统的灵活性:
    1. Subsystem 可以在不影响 Client 的情况下自由修改。
  1. 提高可维护性:
    1. Subsystem 的接口变化只需要修改 Facade,而不需要修改 Client 代码

缺点

  1. 外观类可能会成为上帝对象 (了解过多或者负责过多的对象)
  2. 在一定程度上违反开闭原则,子系统添加新功能,可能需要修改外观类。
  • 解决:
    • 使用抽象外观类 :将 Facade定义为一个 abstract class或 interface,提供基本的功能接口。每次扩展时,可以通过创建新的外观类来实现拓展,替代了直接修改原有的 Facade。

      // 抽象外观类
      public abstract class AbstractSmartHomeFacade {
      public abstract void startMode();
      public abstract void stopMode();
      }

      // 具体实现类
      public class SmartHomeFacade extends AbstractSmartHomeFacade {
      private Light light;
      private SoundSystem soundSystem;
      private AirConditioner airConditioner;

      复制代码
      public SmartHomeFacade() {
          this.light = new Light();
          this.soundSystem = new SoundSystem();
          this.airConditioner = new AirConditioner();
      }
      
      @Override
      public void startMode() {
          light.turnOn();
          soundSystem.playMusic();
          airConditioner.turnOn();
      }
      
      @Override
      public void stopMode() {
          light.turnOff();
          soundSystem.stopMusic();
          airConditioner.turnOff();
      }

      }

      // 新增扩展外观类
      public class AdvancedSmartHomeFacade extends SmartHomeFacade {
      private Curtain curtain;

      复制代码
      public AdvancedSmartHomeFacade() {
          super();
          this.curtain = new Curtain();
      }
      
      @Override
      public void startMode() {
          super.startMode();
          curtain.close();
      }
      
      @Override
      public void stopMode() {
          super.stopMode();
          curtain.open();
      }

      }

    • 使用组合代替继承 :通过组合的方式,将新增的功能封装成独立的类,再将其组合进外观类,替代直接 Facade的代码。

      public class SmartHomeFacade {
      private List subsystems = new ArrayList<>();

      复制代码
      public void addSubsystem(Object subsystem) {
          subsystems.add(subsystem);
      }
      
      public void startAll() {
          for (Object subsystem : subsystems) {
              // 统一调用子系统的 start 方法
              // 可以通过接口或反射实现
          }
      }
      
      public void stopAll() {
          for (Object subsystem : subsystems) {
              // 统一调用子系统的 stop 方法
          }
      }

      }

      public class Client {
      public static void main(String[] args) {
      // 创建外观类
      SmartHomeFacade facade = new SmartHomeFacade();

      复制代码
          // 添加子系统
          facade.addSubsystem(new Light());
          facade.addSubsystem(new SoundSystem());
          facade.addSubsystem(new AirConditioner());
          // 省略
          // ................................................
      
          // 启动所有子系统
          System.out.println("Starting all subsystems...");
          facade.startAll();
      
          // 停止所有子系统
          System.out.println("\nStopping all subsystems...");
          facade.stopAll();
      }

      }

      案例实现

      在源码中的应用------Tomcat

      • 使用 Tomcat 作为 Web 容器时,Coyote 接受浏览器发送的请求,封装为 Request。为了符合 Servlet API 标准,Tomcat 使用 RequestFacade(实现了 HttpServletRequest接口)对内部的 Request对象进行包装。
      • 所以,在 Servlet 中开发者接触到的 Request其实是 RequestFacade,通过 Facade 模式,隐藏了实现细节,确保安全性和封装性。

      与其他设计模式的关系

      • 外观模式为现有对象定义了一个新接口, 适配器模式则会试图运用已有的接口。适配器 通常只封装一个对象,外观通常会作用于整个对象子系统上。
      • 当只需对客户端代码隐藏子系统创建对象的方式时, 你可以使用抽象工厂模式来代替外观。
      • 享元模式展示了如何生成大量的小型对象, 外观则展示了如何用一个对象来代表整个子系统。
      • 外观和中介者模式的职责类似: 它们都尝试在大量紧密耦合的类中组织起合作。
        • 外 观为子系统中的所有对象定义了一个简单接口, 但是它不提供任何新功能。 子系统本身不会意识到外观的存在。 子系统中的对象可以直接进行交流。
        • 中 介 者将系统中组件的沟通行为中心化。 各组件只知道中介者对象, 无法直接相互交流。
      • 外观类通常可以转换为单例模式类, 因为在大部分情况下一个外观对象就足够了。
      • 外观与代理模式的相似之处在于它们都缓存了一个复杂实体并自行对其进行初始化。 代理 与其服务对象遵循同一接口, 使得自己和服务对象可以互换, 在这一点上它与外观不同。
      相关推荐
      苏三说技术23 分钟前
      继PHP之后,Java也爆出了重大安全漏洞!
      后端
      念何架构之路28 分钟前
      Go reflect 包:结构体反射 —Field、Tag 与 VisibleFields
      java·数据库·golang
      liangbo71 小时前
      03-JDK 10/11 局部变量推断与 LTS:var 到底该怎么用
      java
      Percep_gan1 小时前
      调用天翼云的对象存储ZOS,很详细
      java·1024程序员节
      若亦_2 小时前
      数据结构>>优先级队列(堆)
      java·数据结构
      程序猿乐锅2 小时前
      【从0-1学习Python | 第七篇】Python 面向对象基础
      开发语言·笔记·后端·python·学习·1024程序员节
      Wang's Blog2 小时前
      Java 项目实战: 交友项目 - 查询圈子个人动态:查询我的动态需求分析
      java·开发语言·交友
      denlaku2 小时前
      JDK 23 新特性详解
      java
      code2cat2 小时前
      Java高级篇之Hibernate:让对象映射到表,理解持久化与事务
      java·后端·hibernate·1024程序员节
      Younglina2 小时前
      三个晚上,我和 WorkBuddy 做了个生图网站
      前端·后端·ai编程