设计模式之外观模式

设计模式之外观模式

甜蜜的家庭影院

在我们进入外观模式的细节之前,让我们看一个风行全美的热潮:建立自己的家庭影院。你组装了一套杀手级的系统,内含DVD播放器、投影机、自动屏幕、环绕立体声,甚至还有爆米花机。

看看我们的组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* 功放
* @author Mr.zxb
* @date 2020-08-24 20:59:55
*/
public class Amplifier {
private Tuner tuner;
private DvdPlayer dvdPlayer;
private CdPlayer cdPlayer;

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

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

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

public void setDvd(DvdPlayer dvdPlayer) {
this.dvdPlayer = dvdPlayer;
}

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

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

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

public void setVolume(int i) {
System.out.println("Amplifier setVolume " + i);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* DVD播放机
* @author Mr.zxb
* @date 2020-08-24 21:02:58
*/
public class DvdPlayer {
private Amplifier amplifier;
public void on() {
System.out.println("DvdPlayer on");
}
public void off() {
System.out.println("DvdPlayer off");
}

public void eject() {
System.out.println("DvdPlayer eject");
}

public void pause() {
System.out.println("DvdPlayer pause");
}

public void play(String movie) {
System.out.println("DvdPlayer play " + movie);
}

public void setSurroundAudio() {
System.out.println("DvdPlayer setSurroundAudio");
}

public void setTwoChannelAudio() {
System.out.println("DvdPlayer setTwoChannelAudio");
}

public void stop() {
System.out.println("DvdPlayer stop");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 投影仪
* @author Mr.zxb
* @date 2020-08-24 21:04:00
*/
public class Projector {
private DvdPlayer dvdPlayer;

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

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

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

public void wideScreenMode() {
System.out.println("Projector wideScreenMode");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 剧院灯
* @author Mr.zxb
* @date 2020-08-24 21:06:14
*/
public class TheaterLights {
public void on() {
System.out.println("TheaterLights on");
}

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

public void dim(int i) {
System.out.println("TheaterLights dim " + i);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 爆米花
* @author Mr.zxb
* @date 2020-08-24 21:05:34
*/
public class PopcornPopper {
public void on() {
System.out.println("PopcornPopper on");
}

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

public void pop() {
System.out.println("PopcornPopper pop");
}
}

你花了几个星期布线、挂上投影机、连接所有的装置并进行微调。现在,你准备开始享受一部电影……

观赏电影(用困难的方式)

挑选一部DVD影片,然后先执行一些任务。

打开爆米花机、开始爆米花、将灯光调暗、放下屏幕、打开投影机、将投影机 的输入切换到DVD、将投影机设置在宽屏模式、打开功放、将功放的输入设置为DVD、将功放设置为环绕立体声、调节功放音量、打开DVD播放器、开始播放DVD。。。

累死人了,打开这么多开关。。。。没错看完电影还要将这些关掉。让我们看看外观模式如何来解决这团混乱,好让你能轻易的享受电影。。。

构造家庭影院的外观

让我们逐步构造家庭影院外观:第一步是使用组合让外观能够访问子系统中所有的组件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
* 家庭影院
* @author Mr.zxb
* @date 2020-08-24 21:09:03
*/
public class HomeTheaterFacade {
private final Amplifier amplifier;
private final Tuner tuner;
private final DvdPlayer dvdPlayer;
private final CdPlayer cdPlayer;
private final Projector projector;
private final TheaterLights theaterLights;
private final Screen screen;
private final PopcornPopper popcornPopper;

public HomeTheaterFacade(Amplifier amplifier, Tuner tuner, DvdPlayer dvdPlayer,
CdPlayer cdPlayer, Projector projector,
TheaterLights theaterLights, Screen screen,
PopcornPopper popcornPopper) {
this.amplifier = amplifier;
this.tuner = tuner;
this.dvdPlayer = dvdPlayer;
this.cdPlayer = cdPlayer;
this.projector = projector;
this.theaterLights = theaterLights;
this.screen = screen;
this.popcornPopper = popcornPopper;
}

// 将我们之前手动进行的每项任务依次处理。请注意,每项任务都是委托子系统中相应的组件处理的
public void watchMovie(String movie) {
System.out.println("Get Ready to watch a movie....");
popcornPopper.on();
popcornPopper.pop();
theaterLights.dim(10);
screen.down();
projector.on();
projector.wideScreenMode();
amplifier.on();
amplifier.setDvd(dvdPlayer);
amplifier.setSurroundSound();
amplifier.setVolume(5);
dvdPlayer.on();
dvdPlayer.play(movie);
}

// 关闭一切
public void endMovie() {
System.out.println("Shutting movie theater down...");
popcornPopper.off();
theaterLights.on();
screen.up();
projector.off();
amplifier.off();
dvdPlayer.stop();
dvdPlayer.eject();
dvdPlayer.off();
}
}

观赏电影(用轻松的方式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class HomeTheaterTestDrive {
public static void main(String[] args) {
// 实例化各组件
Amplifier amplifier = new Amplifier();
Tuner tuner = new Tuner();
DvdPlayer dvdPlayer = new DvdPlayer();
CdPlayer cdPlayer = new CdPlayer();
Projector projector = new Projector();
TheaterLights theaterLights = new TheaterLights();
Screen screen = new Screen();
PopcornPopper popcornPopper = new PopcornPopper();

// 根据子系统所有的组件来实例化外观
HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade(amplifier, tuner, dvdPlayer,
cdPlayer, projector, theaterLights, screen, popcornPopper);

// 使用简化接口来开启电影
homeTheaterFacade.watchMovie("Raiders of the Lost Ark");
// 然后关闭电影
homeTheaterFacade.endMovie();
}
}

定义外观模式

外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。

“最少知识”原则

最少知识原则告诉我们要减少对象之间的交互,只留下几个“密友”。这个原则通常是这么说的:

最少知识原则:只和你的密友谈话。

这个原则希望我们在设计中,不要让太多的类耦合在一起,免得修改系统中的一部分,会影响到其他部分。如果许多类之间相互依赖,那么这个系统就会变成一个易碎的系统,它需要花许多成本维护,也会因为太复杂而不容易被其他人理解。

总结

  • 当需要简化并统一一个很大的接口或则一群复杂的接口时,使用外观
  • 外观将客户从一个复杂的子系统中解耦
  • 实现一个外观,需要将子系统组合进外观中,然后将工作委托给子系统执行
  • 你可以为一个子系统实现一个以上的外观