外观模式 (Facade Pattern)

外观模式 (Facade Pattern)

概述

外观模式是一种结构型设计模式,它为子系统中的一组接口提供一个一致的界面。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

意图

  • 为子系统中的一组接口提供一个一致的界面
  • 外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用

适用场景

  • 当你要为一个复杂子系统提供一个简单接口时
  • 客户程序与抽象类的实现部分之间存在着很大的依赖性
  • 当你需要构建一个层次结构的子系统时,使用外观模式定义子系统中每层的入口点

结构

复制代码
┌─────────────┐          ┌─────────────┐
│   Client    │──────────>│   Facade    │
├─────────────┤          ├─────────────┤
│             │          │ + operation()│
└─────────────┘          └─────────────┘
                                 ▲
                                 │
┌─────────────┐          ┌─────────────┐
│SubsystemClass1│         │SubsystemClass2│
├─────────────┤          ├─────────────┤
│ + operation()│          │ + operation()│
└─────────────┘          └─────────────┘

┌─────────────┐
│SubsystemClass3│
├─────────────┤
│ + operation()│
└─────────────┘

参与者

  • Facade:知道哪些子系统类负责处理请求,将客户的请求代理给适当的子系统对象
  • Subsystem Classes:实现子系统的功能,处理由Facade对象指派的任务
  • Client:通过Facade接口与子系统交互

示例代码

下面是一个完整的外观模式示例,以家庭影院系统为例:

java 复制代码
// Subsystem Classes - 子系统类
public class Amplifier {
    private String description;
    
    public Amplifier(String description) {
        this.description = description;
    }
    
    public void on() {
        System.out.println(description + " 功放打开");
    }
    
    public void off() {
        System.out.println(description + " 功放关闭");
    }
    
    public void setSurroundSound() {
        System.out.println(description + " 功放设置为环绕声");
    }
    
    public void setVolume(int level) {
        System.out.println(description + " 功放音量设置为 " + level);
    }
}

public class DvdPlayer {
    private String description;
    
    public DvdPlayer(String description) {
        this.description = description;
    }
    
    public void on() {
        System.out.println(description + " DVD播放器打开");
    }
    
    public void off() {
        System.out.println(description + " DVD播放器关闭");
    }
    
    public void play(String movie) {
        System.out.println(description + " DVD播放器正在播放 \"" + movie + "\"");
    }
    
    public void stop() {
        System.out.println(description + " DVD播放器停止");
    }
    
    public void eject() {
        System.out.println(description + " DVD播放器弹出");
    }
}

public class Projector {
    private String description;
    
    public Projector(String description) {
        this.description = description;
    }
    
    public void on() {
        System.out.println(description + " 投影仪打开");
    }
    
    public void off() {
        System.out.println(description + " 投影仪关闭");
    }
    
    public void wideScreenMode() {
        System.out.println(description + " 投影仪设置为宽屏模式");
    }
}

public class Screen {
    private String description;
    
    public Screen(String description) {
        this.description = description;
    }
    
    public void up() {
        System.out.println(description + " 屏幕升起");
    }
    
    public void down() {
        System.out.println(description + " 屏幕降下");
    }
}

public class TheaterLights {
    private String description;
    
    public TheaterLights(String description) {
        this.description = description;
    }
    
    public void on() {
        System.out.println(description + " 灯光打开");
    }
    
    public void off() {
        System.out.println(description + " 灯光关闭");
    }
    
    public void dim(int level) {
        System.out.println(description + " 灯光调暗到 " + level + "%");
    }
}

public class PopcornPopper {
    private String description;
    
    public PopcornPopper(String description) {
        this.description = description;
    }
    
    public void on() {
        System.out.println(description + " 爆米花机打开");
    }
    
    public void off() {
        System.out.println(description + " 爆米花机关闭");
    }
    
    public void pop() {
        System.out.println(description + " 爆米花机正在制作爆米花");
    }
}

// Facade - 外观类
public class HomeTheaterFacade {
    private Amplifier amplifier;
    private DvdPlayer dvdPlayer;
    private Projector projector;
    private Screen screen;
    private TheaterLights lights;
    private PopcornPopper popper;
    
    public HomeTheaterFacade(Amplifier amplifier, DvdPlayer dvdPlayer, Projector projector, 
                            Screen screen, TheaterLights lights, PopcornPopper popper) {
        this.amplifier = amplifier;
        this.dvdPlayer = dvdPlayer;
        this.projector = projector;
        this.screen = screen;
        this.lights = lights;
        this.popper = popper;
    }
    
    public void watchMovie(String movie) {
        System.out.println("准备观看电影...");
        popper.on();
        popper.pop();
        lights.dim(10);
        screen.down();
        projector.on();
        projector.wideScreenMode();
        amplifier.on();
        amplifier.setSurroundSound();
        amplifier.setVolume(5);
        dvdPlayer.on();
        dvdPlayer.play(movie);
    }
    
    public void endMovie() {
        System.out.println("关闭电影系统...");
        popper.off();
        lights.on();
        screen.up();
        projector.off();
        amplifier.off();
        dvdPlayer.stop();
        dvdPlayer.eject();
        dvdPlayer.off();
    }
}

// Client - 客户端
public class Client {
    public static void main(String[] args) {
        // 创建子系统组件
        Amplifier amplifier = new Amplifier("顶级");
        DvdPlayer dvdPlayer = new DvdPlayer("顶级");
        Projector projector = new Projector("顶级");
        Screen screen = new Screen("巨大");
        TheaterLights lights = new TheaterLights("影院");
        PopcornPopper popper = new PopcornPopper("豪华");
        
        // 创建外观
        HomeTheaterFacade homeTheater = new HomeTheaterFacade(amplifier, dvdPlayer, projector, screen, lights, popper);
        
        // 使用外观观看电影
        homeTheater.watchMovie("复仇者联盟");
        System.out.println("\n");
        
        // 使用外观关闭电影
        homeTheater.endMovie();
    }
}

另一个示例 - 计算机启动

java 复制代码
// Subsystem Classes - 子系统类
public class CPU {
    public void freeze() {
        System.out.println("CPU冻结");
    }
    
    public void jump(long position) {
        System.out.println("CPU跳转到位置: " + position);
    }
    
    public void execute() {
        System.out.println("CPU执行指令");
    }
}

public class Memory {
    public void load(long position, byte[] data) {
        System.out.println("内存加载数据到位置: " + position);
    }
}

public class HardDrive {
    public byte[] read(long lba, int size) {
        System.out.println("硬盘读取数据,LBA: " + lba + ", 大小: " + size);
        return new byte[size];
    }
}

public class GPU {
    public void on() {
        System.out.println("GPU打开");
    }
    
    public void render() {
        System.out.println("GPU渲染图像");
    }
}

public class SoundCard {
    public void on() {
        System.out.println("声卡打开");
    }
    
    public void playSound() {
        System.out.println("声卡播放声音");
    }
}

// Facade - 外观类
public class ComputerFacade {
    private CPU cpu;
    private Memory memory;
    private HardDrive hardDrive;
    private GPU gpu;
    private SoundCard soundCard;
    
    public ComputerFacade() {
        this.cpu = new CPU();
        this.memory = new Memory();
        this.hardDrive = new HardDrive();
        this.gpu = new GPU();
        this.soundCard = new SoundCard();
    }
    
    public void start() {
        System.out.println("计算机启动中...");
        
        gpu.on();
        soundCard.on();
        
        cpu.freeze();
        byte[] bootData = hardDrive.read(0, 1024);
        memory.load(0, bootData);
        cpu.jump(0);
        cpu.execute();
        
        gpu.render();
        soundCard.playSound();
        
        System.out.println("计算机启动完成");
    }
    
    public void shutdown() {
        System.out.println("计算机关闭中...");
        // 关闭各个组件
        System.out.println("计算机关闭完成");
    }
}

// Client - 客户端
public class Client {
    public static void main(String[] args) {
        ComputerFacade computer = new ComputerFacade();
        computer.start();
        System.out.println();
        computer.shutdown();
    }
}

优缺点

优点

  1. 它对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加方便
  2. 它实现了子系统与客户之间的松耦合关系,这使得子系统的组件变化不会影响到调用它的客户
  3. 外观模式有助于划分层次结构,对复杂的子系统提供统一的接口,降低了系统的复杂度

缺点

  1. 不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性
  2. 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了"开闭原则"

相关模式

  • 抽象工厂模式:抽象工厂模式可以与外观模式一起使用,外观模式可以使用抽象工厂来获取子系统组件
  • 中介者模式:中介者模式与外观模式都封装了对象间的交互,但中介者模式关注的是对象间的多对多交互,而外观模式关注的是简化子系统的接口
  • 单例模式:外观对象通常可以设计为单例

实际应用

  • Java中的javax.faces.context.FacesContext
  • Java中的java.util.logging包
  • Spring框架中的ApplicationContext
  • Tomcat中的Request和Response对象
  • 各种API的简化接口

外观模式与适配器模式的区别

  • 外观模式:定义一个新的接口,简化子系统的使用,通常在系统设计阶段使用
  • 适配器模式:使已有的接口能够协同工作,通常在系统维护阶段使用

外观模式关注的是简化接口,而适配器模式关注的是接口的转换。

注意事项

  1. 外观模式并不封装子系统,只是提供简化的接口,客户端仍然可以直接访问子系统
  2. 外观模式本身并不增加新的功能,只是简化了现有的功能
  3. 一个系统可以有多个外观类,每个外观类负责不同的功能
  4. 外观模式有助于分层设计,可以在每一层都定义一个外观类,简化层与层之间的交互
相关推荐
wVAkMDmzEWcm6 天前
易语言实现Jlink烧录程序:打造自动化烧录利器
外观模式
蔺太微19 天前
外观模式(Facade Pattern)
设计模式·外观模式
小码过河.20 天前
设计模式——外观模式
设计模式·外观模式
Engineer邓祥浩1 个月前
设计模式学习(12) 23-10 外观模式
学习·设计模式·外观模式
Geoking.1 个月前
【设计模式】外观模式(Facade)详解
java·设计模式·外观模式
sxlishaobin1 个月前
设计模式之外观模式
java·设计模式·外观模式
阿闽ooo1 个月前
外观模式:从家庭电源控制看“简化接口“的设计智慧
c++·设计模式·外观模式
.简.简.单.单.1 个月前
Design Patterns In Modern C++ 中文版翻译 第十章 外观模式
c++·设计模式·外观模式
JavaBoy_XJ1 个月前
结构型-外观模式
外观模式