外观模式


目录

  1. 外观模式简介

  2. 外观模式的意图

  3. 外观模式的结构

  4. 外观模式的实现

  5. 外观模式的适用场景

  6. 外观模式的优缺点

  7. 外观模式的实际应用实例

  8. 外观模式与其他模式的比较

  9. 总结

  10. 参考资料


1. 外观模式简介

外观模式(Facade Pattern)是一种结构型设计模式,它为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用。外观模式通过创建一个外观类,封装复杂的子系统,使客户端只需与外观类交互,而无需直接与子系统的各个部分打交道。

关键点:

  • 简化接口:提供一个简单的接口,隐藏子系统的复杂性。

  • 解耦系统:客户端与子系统之间的依赖关系被外观类解耦。

  • 提高可维护性:修改子系统的内部实现不会影响客户端,只需修改外观类。


2. 外观模式的意图

外观模式的主要目的是:

  • 为复杂子系统提供统一的接口:通过一个外观类,简化客户端与子系统的交互。

  • 降低系统的复杂度:隐藏子系统的复杂性,使客户端更容易使用系统。

  • 提高系统的可用性和可维护性:客户端无需了解子系统的内部细节,系统结构更加清晰。


3. 外观模式的结构

3.1. 结构组成

外观模式主要由以下几个角色组成:

  1. Facade(外观类):知道哪些子系统类负责处理请求,并将客户端的请求代理给相应的子系统类。

  2. Subsystem Classes(子系统类):实现子系统的功能,外观类将客户端的请求委托给这些类。

  3. Client(客户端):通过外观类与子系统交互,而无需直接调用子系统类。

角色关系:

  • Facade 持有对 Subsystem Classes 的引用。

  • Client 仅与 Facade 交互,不直接与 Subsystem Classes 交互。

3.2. UML类图

以下是外观模式的简化UML类图:

+------------------+         +------------------+
|      Client      |         |      Facade      |
+------------------+         +------------------+
|                  |         | - subsystem1     |
|                  |         | - subsystem2     |
|                  |         | - subsystem3     |
|                  |         | + operation()    |
+------------------+         +------------------+
           |                          |
           |                          |
           v                          v
+------------------+    +------------------+    +------------------+
|  Subsystem1      |    |  Subsystem2      |    |  Subsystem3      |
+------------------+    +------------------+    +------------------+
| + operation1()   |    | + operation2()   |    | + operation3()   |
+------------------+    +------------------+    +------------------+

说明:

  • Client 调用 Facadeoperation() 方法。

  • Facadeoperation() 方法中调用 Subsystem1Subsystem2Subsystem3 的各自方法。


4. 外观模式的实现

以下示例将展示如何在Java和Python中实现外观模式。

4.1. Java 实现示例

以下是一个使用外观模式实现家庭影院系统的示例,其中HomeTheaterFacade作为外观类,封装了多个子系统类(如AmplifierTunerStreamingPlayerProjectorScreenTheaterLights)。

// 子系统类1
class Amplifier {
    public void on() {
        System.out.println("Amplifier is on.");
    }

    public void setStreamingPlayer(StreamingPlayer player) {
        System.out.println("Amplifier setting Streaming Player.");
    }

    public void setSurroundSound() {
        System.out.println("Amplifier surround sound set.");
    }

    public void setVolume(int level) {
        System.out.println("Amplifier volume set to " + level + ".");
    }

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

// 子系统类2
class Tuner {
    public void on() {
        System.out.println("Tuner is on.");
    }

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

// 子系统类3
class StreamingPlayer {
    public void on() {
        System.out.println("Streaming Player is on.");
    }

    public void play(String movie) {
        System.out.println("Streaming Player is playing \"" + movie + "\".");
    }

    public void stop() {
        System.out.println("Streaming Player has stopped.");
    }

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

// 子系统类4
class Projector {
    public void on() {
        System.out.println("Projector is on.");
    }

    public void wideScreenMode() {
        System.out.println("Projector in widescreen mode.");
    }

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

// 子系统类5
class Screen {
    public void down() {
        System.out.println("Screen is down.");
    }

    public void up() {
        System.out.println("Screen is up.");
    }
}

// 子系统类6
class TheaterLights {
    public void dim(int level) {
        System.out.println("Theater Ceiling Lights dimming to " + level + "%.");
    }

    public void on() {
        System.out.println("Theater Ceiling Lights are on.");
    }
}

// 外观类
class HomeTheaterFacade {
    private Amplifier amp;
    private Tuner tuner;
    private StreamingPlayer player;
    private Projector projector;
    private Screen screen;
    private TheaterLights lights;

    public HomeTheaterFacade(Amplifier amp, Tuner tuner, StreamingPlayer player,
                             Projector projector, Screen screen, TheaterLights lights) {
        this.amp = amp;
        this.tuner = tuner;
        this.player = player;
        this.projector = projector;
        this.screen = screen;
        this.lights = lights;
    }

    public void watchMovie(String movie) {
        System.out.println("Get ready to watch a movie...");
        lights.dim(10);
        screen.down();
        projector.on();
        projector.wideScreenMode();
        amp.on();
        amp.setStreamingPlayer(player);
        amp.setSurroundSound();
        amp.setVolume(5);
        player.on();
        player.play(movie);
    }

    public void endMovie() {
        System.out.println("Shutting movie theater down...");
        lights.on();
        screen.up();
        projector.off();
        amp.off();
        player.stop();
        player.off();
    }
}

// 客户端代码
public class FacadePatternDemo {
    public static void main(String[] args) {
        Amplifier amp = new Amplifier();
        Tuner tuner = new Tuner();
        StreamingPlayer player = new StreamingPlayer();
        Projector projector = new Projector();
        Screen screen = new Screen();
        TheaterLights lights = new TheaterLights();

        HomeTheaterFacade homeTheater = new HomeTheaterFacade(amp, tuner, player, projector, screen, lights);

        homeTheater.watchMovie("Inception");
        System.out.println();
        homeTheater.endMovie();
    }
}

输出:

Get ready to watch a movie...
Theater Ceiling Lights dimming to 10%.
Screen is down.
Projector is on.
Projector in widescreen mode.
Amplifier is on.
Amplifier setting Streaming Player.
Amplifier surround sound set.
Amplifier volume set to 5.
Streaming Player is on.
Streaming Player is playing "Inception".

Shutting movie theater down...
Theater Ceiling Lights are on.
Screen is up.
Projector is off.
Amplifier is off.
Streaming Player has stopped.
Streaming Player is off.

4.2. Python 实现示例

以下是使用外观模式实现家庭影院系统的Python示例。

# 子系统类1
class Amplifier:
    def on(self):
        print("Amplifier is on.")

    def set_streaming_player(self, player):
        print("Amplifier setting Streaming Player.")

    def set_surround_sound(self):
        print("Amplifier surround sound set.")

    def set_volume(self, level):
        print(f"Amplifier volume set to {level}.")

    def off(self):
        print("Amplifier is off.")

# 子系统类2
class Tuner:
    def on(self):
        print("Tuner is on.")

    def off(self):
        print("Tuner is off.")

# 子系统类3
class StreamingPlayer:
    def on(self):
        print("Streaming Player is on.")

    def play(self, movie):
        print(f"Streaming Player is playing \"{movie}\".")

    def stop(self):
        print("Streaming Player has stopped.")

    def off(self):
        print("Streaming Player is off.")

# 子系统类4
class Projector:
    def on(self):
        print("Projector is on.")

    def wide_screen_mode(self):
        print("Projector in widescreen mode.")

    def off(self):
        print("Projector is off.")

# 子系统类5
class Screen:
    def down(self):
        print("Screen is down.")

    def up(self):
        print("Screen is up.")

# 子系统类6
class TheaterLights:
    def dim(self, level):
        print(f"Theater Ceiling Lights dimming to {level}%.")

    def on(self):
        print("Theater Ceiling Lights are on.")

# 外观类
class HomeTheaterFacade:
    def __init__(self, amp, tuner, player, projector, screen, lights):
        self.amp = amp
        self.tuner = tuner
        self.player = player
        self.projector = projector
        self.screen = screen
        self.lights = lights

    def watch_movie(self, movie):
        print("Get ready to watch a movie...")
        self.lights.dim(10)
        self.screen.down()
        self.projector.on()
        self.projector.wide_screen_mode()
        self.amp.on()
        self.amp.set_streaming_player(self.player)
        self.amp.set_surround_sound()
        self.amp.set_volume(5)
        self.player.on()
        self.player.play(movie)

    def end_movie(self):
        print("Shutting movie theater down...")
        self.lights.on()
        self.screen.up()
        self.projector.off()
        self.amp.off()
        self.player.stop()
        self.player.off()

# 客户端代码
if __name__ == "__main__":
    amp = Amplifier()
    tuner = Tuner()
    player = StreamingPlayer()
    projector = Projector()
    screen = Screen()
    lights = TheaterLights()

    home_theater = HomeTheaterFacade(amp, tuner, player, projector, screen, lights)

    home_theater.watch_movie("Inception")
    print()
    home_theater.end_movie()

输出:

Get ready to watch a movie...
Theater Ceiling Lights dimming to 10%.
Screen is down.
Projector is on.
Projector in widescreen mode.
Amplifier is on.
Amplifier setting Streaming Player.
Amplifier surround sound set.
Amplifier volume set to 5.
Streaming Player is on.
Streaming Player is playing "Inception".

Shutting movie theater down...
Theater Ceiling Lights are on.
Screen is up.
Projector is off.
Amplifier is off.
Streaming Player has stopped.
Streaming Player is off.

5. 外观模式的适用场景

外观模式适用于以下场景:

  1. 简化复杂系统的接口:当你需要简化一个复杂子系统的接口,使得客户端更容易使用时。

  2. 降低系统的复杂性:通过提供一个统一的外观类,隐藏子系统的复杂性,减少客户端与子系统的依赖。

  3. 为多个子系统提供统一的入口:在需要多个子系统协同工作时,通过外观类协调各个子系统的交互。

  4. 系统独立于子系统的实现:客户端无需了解子系统的具体实现细节,只需通过外观类进行交互。

示例应用场景:

  • 家庭影院系统:通过外观类控制多个子系统(如音响、播放器、投影仪等)。

  • 数据库访问层:通过外观类提供统一的数据库操作接口,隐藏底层数据库驱动的复杂性。

  • 编译器:通过外观类简化编译过程,协调词法分析、语法分析、代码生成等多个子系统。

  • 图形绘制库:提供简化的接口,隐藏复杂的图形渲染逻辑。


6. 外观模式的优缺点

6.1. 优点

  1. 简化接口:通过提供一个统一的高层接口,简化了客户端与子系统的交互。

  2. 降低系统复杂性:隐藏子系统的复杂性,使系统更易于使用和维护。

  3. 解耦系统:客户端与子系统之间的依赖关系被外观类解耦,增强了系统的灵活性。

  4. 提高可维护性:子系统的内部实现可以独立于客户端进行修改和优化,无需影响客户端代码。

  5. 促进分层设计:外观模式有助于构建清晰的系统分层结构,每一层通过外观类进行通信。

6.2. 缺点

  1. 可能导致设计过度简化:如果子系统的功能过于简单,使用外观模式可能显得冗余。

  2. 可能限制子系统的灵活性:客户端只能通过外观类提供的接口与子系统交互,无法直接访问子系统的高级功能。

  3. 增加系统层次:引入外观类会增加系统的类数量,可能导致项目结构复杂。

  4. 依赖外观类:系统的灵活性在一定程度上依赖于外观类的设计和实现。


7. 外观模式的实际应用实例

7.1. 家庭影院系统

在家庭影院系统中,外观模式可以用来整合多个子系统(如音响、播放器、投影仪、屏幕和灯光等)。通过HomeTheaterFacade类,用户可以通过简单的方法(如watchMovie()endMovie())来控制整个家庭影院系统,而无需分别操作每个子系统。

7.2. 数据库访问层

在一个复杂的数据库访问层中,可能涉及多个子系统(如连接池管理、事务管理、查询优化等)。通过外观模式,可以创建一个DatabaseFacade类,提供统一的接口(如executeQuery()beginTransaction()等),简化客户端的数据库操作。

7.3. 编译器设计

在编译器的设计中,外观模式可以用于简化编译过程。通过一个CompilerFacade类,协调词法分析、语法分析、语义分析和代码生成等多个子系统,提供一个统一的编译接口(如compile(sourceCode))。

7.4. 图形绘制库

在图形绘制库中,外观模式可以用来提供简化的绘图接口。通过一个GraphicsFacade类,用户可以通过简单的方法(如drawCircle()drawRectangle()等)来绘制图形,而无需了解底层的渲染逻辑和图形API。


8. 外观模式与其他模式的比较

8.1. 外观模式 vs. 适配器模式

  • 外观模式用于简化接口,为复杂的子系统提供一个统一的高层接口,强调的是系统的简化和整合

  • 适配器模式用于接口转换,使得不兼容的接口能够协同工作,强调的是接口的兼容性和转换

关键区别:

  • 目的不同:外观模式是为了简化和整合系统接口,适配器模式是为了让不兼容的接口协同工作。

  • 结构不同:外观模式通常是单一的外观类与多个子系统类之间的关系,适配器模式是一个适配器类与一个或多个被适配类之间的关系。

8.2. 外观模式 vs. 装饰者模式

  • 外观模式用于简化接口,为多个子系统提供一个统一的高层接口,强调的是系统的整合和简化

  • 装饰者模式用于动态地为对象添加职责,增强对象的功能,强调的是功能的扩展和增强

关键区别:

  • 目的不同:外观模式是为了简化和整合系统接口,装饰者模式是为了动态地为对象添加新功能。

  • 应用场景不同:外观模式通常用于系统的入口,装饰者模式用于对象的功能扩展。

8.3. 外观模式 vs. 代理模式

  • 外观模式用于简化接口,为多个子系统提供一个统一的高层接口,强调的是系统的整合和简化

  • 代理模式用于控制对对象的访问,提供一个替代对象来管理对实际对象的访问,强调的是访问控制和对象管理

关键区别:

  • 目的不同:外观模式是为了简化和整合系统接口,代理模式是为了控制对对象的访问。

  • 功能不同:外观模式通常不改变子系统的功能,代理模式可能在访问前后添加额外的功能(如权限检查、懒加载等)。

8.4. 外观模式 vs. 组合模式

  • 外观模式用于简化接口,为复杂的子系统提供一个统一的高层接口,强调的是系统的整合和简化

  • 组合模式用于构建部分-整体的层次结构,允许客户端以一致的方式处理单个对象和组合对象,强调的是对象的组织结构

关键区别:

  • 目的不同:外观模式是为了简化和整合系统接口,组合模式是为了表示对象的层次结构。

  • 应用场景不同:外观模式用于系统的外部接口,组合模式用于内部对象的组织和管理。


9. 总结

外观模式(Facade Pattern) 通过为复杂的子系统提供一个统一的高层接口,简化了客户端与子系统的交互,降低了系统的复杂性和耦合度。外观模式适用于需要简化复杂系统接口、提高系统可用性和可维护性的场景。

关键学习点回顾:

  1. 理解外观模式的核心概念:为复杂子系统提供统一的高层接口,简化客户端与子系统的交互。

  2. 掌握外观模式的结构:包括Facade、Subsystem Classes和Client之间的关系。

  3. 识别适用的应用场景:简化复杂系统接口、降低系统复杂性、为多个子系统提供统一入口等。

  4. 认识外观模式的优缺点:简化接口、解耦系统、提高可维护性;但可能导致系统层次增加、限制子系统的灵活性。

  5. 实际应用中的外观模式实例:家庭影院系统、数据库访问层、编译器设计、图形绘制库等。


10. 参考资料