当进行一项操作需要对系统中各个功能模块比较了解,并且需要调用多个命令的时候,我们可以使用外观模式来简化客户端的操作。外观模式创建了一个接口简化而统一的类,用来包装子系统中一个或多个复杂的类,并且由于外观模式是依赖于接口的,因而其解除了客户端与子系统的紧密耦合,当子系统发生改变时客户端就不会受到影响。外观模式主要有三个部分:客户端,外观接口,子系统接口。其结构图如下:
从图中可以看出,本来与客户交流的系统类现在改为与外观接口的子类来交流,而客户只需要与外管接口打交道即可。这里需要说明的是外观模式没有“封装”子系统的类,外观只是提供了简化的接口,所以客户如果觉得有必要,依然可以直接使用子系统的类。并且外观简化接口的同时也可以额外提供一些聪明的功能。
外观模式本质上使用了设计中的一个原则:“最少知识”原则。最少知识原则告诉我们要减少对象之间的交互,只留下几个密友。这个原则希望我们在设计中,不要让太多的类耦合在一起,免得修改系统中的一部分,会影响到其他部分。如果许多类之间相互依赖,那么这个系统就会变成一个易碎的系统,它需要花许多成本维护,也会因为太复杂而不容易被其他人了解。为了避免对象之间的过度耦合,“最少知识”原则提供了几个在对象内书写方法的方针:
- 调用该对象本身;
- 被当做方法的参数而传递进来的对象;
- 此方法所创建或实例化的任何对象;
- 对象的任何组件;
这个原则告诉我们,如果某个对象是调用其他方法的返回结果,我们不应该调用该对象的方法,而应该在返回该对象的方法所在的类中再声明一个方法来操作该对象。这样我们就不需要认识该对象的组件了。“最少知识”原则虽然减少了对象之间的依赖,但是采用这个原则会导致更多的包装类被制造出来,以处理和其他组件的沟通,这可能会导致复杂度和开发时间的增加,并降低运行时的性能。
这里使用一个家庭影院的例子来进行说明。假设你在家里组装了一套观看电影的系统,里面包含DVD播放器、投影仪、自动屏幕、环绕立体声。但是当你想看电影的时候你发现需要打开DVD播放器,放下投影仪,调节自动屏幕,打开环绕立体声,以及插入光盘,开始播放等等操作。这些操作比较复杂,而且有很多的细节其实你每次观影时是不需要考虑的,比如打开DVD播放器和放下投影仪。你想到了使用一个智能遥控器,当输入你想观看的影片时,点击播放那么一切就会准备就绪并自动播放起来,这样会简化你很多的操作,而且也能满足你的需求。这里就用到了外观模式,遥控器就相当于一个外观接口,家庭影院是需要操作的系统,遥控器就实现了你与家庭影院的解耦,使你不必担心过多细节。以下是这个实例的具体代码。
外观接口:
public interface Facade {
void watchMovie(String movie);
void endMovie();
}
外观接口实现类:
public class HomeTheaterFacade implements Facade {
private Amplifier amplifier;
private Tuner tuner;
private DvdPlayer dvdPlayer;
private CdPlayer cdPlayer;
private Projecter projecter;
private TheaterLights lights;
private Screen screen;
private PopcomPopper popper;
public HomeTheaterFacade(Amplifier amplifier, Tuner tuner, DvdPlayer dvdPlayer, CdPlayer cdPlayer, Projecter projecter, TheaterLights lights, Screen screen, PopcomPopper popper) {
this.amplifier = amplifier;
this.tuner = tuner;
this.dvdPlayer = dvdPlayer;
this.cdPlayer = cdPlayer;
this.projecter = projecter;
this.lights = lights;
this.screen = screen;
this.popper = popper;
}
// other methods
@Override
public void watchMovie(String movie) {
System.out.println("Get ready to watch a movie...");
popper.on();
popper.pop();
lights.dim();
screen.down();
projecter.on();
projecter.wideScreenMode();
amplifier.on();
amplifier.setDvd();
amplifier.setSurroundSound();
amplifier.setVolume();
dvdPlayer.on();
dvdPlayer.play();
}
@Override
public void endMovie() {
System.out.println("Shutting movie theater down...");
popper.off();
lights.on();
screen.up();
projecter.off();
amplifier.off();
dvdPlayer.stop();
dvdPlayer.eject();
dvdPlayer.off();
}
}
子系统:
public class Amplifier {
private Tuner tuner;
private DvdPlayer dvdPlayer;
private CdPlayer cdPlayer;
public void on() {}
public void off() {}
public void setCd() {}
public void setDvd() {}
public void setStereoSound() {}
public void setSurroundSound() {}
public void setTuner() {}
public void setVolume() {}
@Override
public String toString() {
return "This is a amplifier";
}
}
public class CdPlayer {
private Amplifier amplifier;
public void on() {}
public void off() {}
public void eject() {}
public void pause() {}
public void play() {}
public void stop() {}
@Override
public String toString() {
return "This is a CD player";
}
}
public class DvdPlayer {
private Amplifier amplifier;
public void on() {}
public void off() {}
public void eject() {}
public void pause() {}
public void play() {}
public void setSurroundAudio() {}
public void setTwoChannelAudio() {}
public void stop() {}
@Override
public String toString() {
return "This is a dvd player";
}
}
public class PopcomPopper {
public void on() {}
public void off() {}
public void pop() {}
@Override
public String toString() {
return "adjust popcom popper";
}
}
public class Projecter {
private DvdPlayer dvdPlayer;
public void on() {}
public void off() {}
public void tvMode() {}
public void wideScreenMode() {}
@Override
public String toString() {
return "adjust projector";
}
}
public class Screen {
public void up() {}
public void down() {}
@Override
public String toString() {
return "adjust screen";
}
}
public class TheaterLights {
public void on() {}
public void off() {}
public void dim() {}
@Override
public String toString() {
return "adjust theater lights";
}
}
public class Tuner {
private Amplifier amplifier;
public void on() {}
public void off() {}
public void setAm() {}
public void setFm() {}
public void setFrequency() {}
@Override
public String toString() {
return "This is a tuner";
}
}
客户端类:
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();
Projecter projecter = new Projecter();
Screen screen = new Screen();
TheaterLights lights = new TheaterLights();
PopcomPopper popper = new PopcomPopper();
Facade facade = new HomeTheaterFacade(amplifier, tuner, dvdPlayer, cdPlayer, projecter, lights, screen, popper);
facade.watchMovie("Raiders of the Lost Ark");
facade.endMovie();
}
}