外观模式
外观模式提供了一个统一的(简单)接口,用来访问子 系统中的一群接口。外观定义了一个高层接口,让系统更 容易使用。
设计原则 #
- 针对接口编程,而不是针对实现编程
- 多用组合,少用继承
- 为交互对象之间的松耦合而努力
- 类应该对拓展开放,而对修改关闭 (开放-关闭原则)
- 依赖抽象,而不依赖具体类 (依赖倒置原则)
- "最少知识原则"——只和你的朋友交谈,不要让太多的类耦合在一起。
最少知识原则:定义 #
在设计系统中,不管是任何对象,都需要注意它所交互的类有哪些,并注意这些类是怎么交互的。
最少知识原则希望,在设计系统的过程中,不要让太多的类交杂在一起,免得修改系统中的一部分, 需要修改其他的部分。如果许多部分相互耦合,系统将会变得复杂、脆弱且不易于维护。
最少知识原则:如何遵循? #
设计系统的过程中,就任何对象而言,在对象的方法内,应该只调用属于一下范畴的方法:
- 对象本身的方法
- 被当作方法参数而传递进来的对象的方法
- 方法内创建或实例化的对象的方法
- 对象的任何组件(域、field)的方法
不要调用从上述方法中返回的对象支持的方法。
请看如下例:
1public float getTemp(){
2 Thermometer thermometer = station.getThermometer();
3 return thermometer.getTemperature();
4}
应该优化为:
1public float getTemp(){
2 return station.getTemperature();
3}
而getTemperature
方法由station
域提供。这样做的好处是,我们
不需要认识Thermometer
对象了。让我们始终保持最小的朋友圈!
最少知识原则:缺点 #
是的,任何事情都是两面。最少知识原则使得系统耦合度降低,减轻了维护成本, 但同时禁止调用中间对象的方法,这不得不让我们制造更多的"包装"类来处理和 其他组件之间的沟通,这可能会导致开发难度增加。
让我们接着回到『外观模式』吧。
UML简图 #

要点 #
- 外观模式中的
Facade
就是那个"密友",它封装了其他要用到的方法,满足了最少知识原则。 - 当要简化并统一一堆接口时,可以使用外观模式。
- 外观将客户从一个复杂的系统中解耦。
- 实现外观,需要将子系统组合进外观中,然后将具体的工作委托给子系统执行。
- 一个复杂的子系统,可以有多个外观。
示例代码 #
外观模式(Facade Pattern)是一种结构型设计模式,它提供了一个统一的接口来访问子系统中的一群接口。外观模式定义了一个高层接口,让子系统更容易使用。
在这个例子中,我们将创建一个简单的家庭影院系统,它包含了放映机、DVD播放器和音响系统等组件。我们将使用外观模式来提供一个简化的接口来控制这些组件,使得客户端不需要与各个子系统的复杂性直接交互。
首先,定义子系统的类:
1// 放映机
2class Projector {
3 void on() {
4 System.out.println("Projector on");
5 }
6 void off() {
7 System.out.println("Projector off");
8 }
9}
10
11// DVD播放器
12class DvdPlayer {
13 void on() {
14 System.out.println("DVD Player on");
15 }
16 void play(String movie) {
17 System.out.println("Playing "" + movie + """);
18 }
19 void stop() {
20 System.out.println("Stop playing DVD");
21 }
22 void off() {
23 System.out.println("DVD Player off");
24 }
25}
26
27
28// 音响系统
29class SoundSystem {
30 void on() {
31 System.out.println("Sound system on");
32 }
33 void setVolume(int level) {
34 System.out.println("Setting volume to " + level);
35 }
36 void off() {
37 System.out.println("Sound system off");
38 }
39}
接下来,创建外观类HomeTheaterFacade,它提供了简单的方法来处理复杂的子系统操作:
1class HomeTheaterFacade {
2 private Projector projector;
3 private DvdPlayer dvdPlayer;
4 private SoundSystem soundSystem;
5
6 public HomeTheaterFacade(
7 Projector projector,
8 DvdPlayer dvdPlayer,
9 SoundSystem soundSystem) {
10 this.projector = projector;
11 this.dvdPlayer = dvdPlayer;
12 this.soundSystem = soundSystem;
13 }
14
15 // 观看电影
16 void watchMovie(String movie) {
17 System.out.println("Get ready to watch a movie...");
18 projector.on();
19 soundSystem.on();
20 soundSystem.setVolume(10);
21 dvdPlayer.on();
22 dvdPlayer.play(movie);
23 }
24
25 // 结束观看
26 void endMovie() {
27 System.out.println("Shutting movie theater down...");
28 projector.off();
29 soundSystem.off();
30 dvdPlayer.stop();
31 dvdPlayer.off();
32 }
33}
最后,客户端代码可以通过外观类来简化对子系统的操作:
1public class FacadePatternDemo {
2 public static void main(String[] args) {
3 Projector projector = new Projector();
4 DvdPlayer dvdPlayer = new DvdPlayer();
5 SoundSystem soundSystem = new SoundSystem();
6
7 HomeTheaterFacade homeTheater
8 = new HomeTheaterFacade(projector, dvdPlayer, soundSystem);
9
10 homeTheater.watchMovie("The Shawshank Redemption");
11 homeTheater.endMovie();
12 }
13}
在这个示例中,HomeTheaterFacade
类提供了一个简化的接口watchMovie
和endMovie
,使得客户端不需要直接与放映机、DVD播放器和音响系统等子系统交互,从而简化了客户端的使用。这就是外观模式的魅力所在,它通过提供一个统一的接口来隐藏子系统的复杂性,使得子系统更加容易使用。
外观模式和宏命令的区别 #
感觉外观模式有点类似于命令模式的宏命令
,都是通过接口组合抽取特定的行为组成
符合需求的逻辑,供客户调用。
不过,外观模式和命令模式的主要诉求不同:
- 命令模式的诉求是将对接口的请求封装成对象,以便于参数化,甚至于结合队列来使用。
- 外观模式的诉求是从一堆可以满足需求的接口中简化逻辑和依赖,简化开发。
- 至于撤销操作,当然也可以通过外观模式来封装。
以下是
chatgpt
总结的外观模式和命令模式的宏命令
的区别
外观模式(Facade Pattern)和命令模式中的“宏命令”(Macro Command)都可以用来简化客户端与一组操作之间的交互,但它们的设计目的、实现方式以及应用场景有所不同。
外观模式 #
外观模式提供了一个统一的高层接口,用来访问子系统中的一群接口。它的主要目的是隐藏系统的复杂性,并提供一个客户端可以访问系统的简单接口。外观模式通常在系统设计初期用于将各个复杂的子系统分离,以减少系统之间的依赖。在系统后期,外观模式可以用于简化客户端的调用,改善子系统之间的交互。
- 意图:提供一个统一的接口,用来访问子系统中的一群接口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
- 应用场景:当你要为一个复杂子系统提供一个简单接口时;当客户端与多个子系统之间存在很大的依赖性时,引入外观模式以将这个子系统与客户端及其他子系统分离,可以提高子系统的独立性和可移植性。
命令模式的“宏命令” #
命令模式将请求封装成对象,从而允许用户使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。宏命令是命令模式的一种扩展,它可以执行一组命令,将多个命令封装为一个“宏”命令来执行。
- 意图:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
- 应用场景:当你需要参数化对象根据请求执行操作时;当你需要实现请求排队系统时;当你需要支持撤销操作时;当你需要将一组操作组合成一个大型操作时(即宏命令)。
区别 #
- 设计目的:
- 外观模式主要是为了隐藏系统的复杂性,提供一个简单的接口给外部调用。
- 命令模式的宏命令则是为了将多个命令组合成一个命令,以便统一调用。
- 实现方式:
- 外观模式通常只有一个外观类,这个类将客户端的请求代理给相应的子系统处理。
- 宏命令则是通过命令模式的结构,将多个命令对象组合成一个宏命令对象,这个宏命令也实现了命令接口,但它的execute方法会依次调用组合命令对象的execute方法。
- 应用场景:
- 外观模式适用于为复杂系统提供一个简单接口的场景。
- 宏命令适用于需要将多个操作组合为一个操作执行,并可能支持撤销/重做等操作的场景。
总的来说,外观模式和命令模式的宏命令虽然都可以简化客户端的操作,但它们的关注点不同:外观模式关注于简化系统接口,命令模式的宏命令关注于将多个操作组合成单一操作,以便统一管理和调用。