您的位置:首页 > 其它

设计模式--外观模式(十二)

2017-08-03 20:57 155 查看
外观模式:提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
  
知识点的梳理:
"最少知识"原则:只和你的密友谈话;
当需要简化并统一一个很大的接口或者一群复杂的接口时,使用外观;
外观将客户从一个复杂的子系统中解耦;
实现一个外观,需要将子系统组合进外观中,然后将工作委托给子系统执行;

可以为一个子系统实现多个外观;
  

先来对比外观模式,适配器模式与装饰者模式



开始介绍前的惯例,来个栗子

需求:建立自己的家庭影院:包含DVD播放器,摄影机,自动屏幕,环绕立体声,爆米花机等等;



观看一部电影,我们需要执行如下步骤:

将以上步骤写成类或者方法:



其它问题:
看完电影后难道还要反向把所有步骤来一遍?
如果要听CD也这么麻烦?
如果要升级硬件系统,还要重新学习操作步骤?

引入外观模式
通过外观模式,实现一个提供合理接口的外观类,可以将一个复杂的子系统变得容易使用。

为家庭影院创建一个外观类HomeTheaterFacade的新类,对外暴露出几个简单的方法,例如watchMovie():



这个外观类将家庭影院的诸多组件视为一个子系统,通过调用这个子系统,来实现watchMovie()方法

现在,你的客户代码可以调用此家庭影院外观所提供的方法,而不必再调用这个子系统的方法。所以,想要看电影只要调用方法watchMovie()方法即可。
外观只是提供更直接的操作,并未将原来的子系统阻隔起来。如果需要子系统类的更高层功能,还是可以使用原来的子系统;

一些问题

外观封装了子系统的类,如果需要低层功能的客户如何接触这些类?
外观只是提供了子系统的简化接口,并没有封装子系统。子系统的功能依然暴露在外;

外观是否可以增加新的功能,或者它只是将没一个请求转由子系统执行?
完全可以;

每个子系统只能有一个外观吗?
可以为一个系统创建许多个外观;

除了提供一个比较简单的接口,外观模式还有其它的优点吗?
外观模式也允许你将客户实现从任何子系统中解耦;
比如,你升级家庭影院,采用全新的接口。如果当初的代码是针对外观而不是针对子系统编写的,现在就不需要改变客户代码,只需要修改外观代码就可以了;

适配器模式和外观模式的差异在于:前者包装一个类,后者代表许多类?
不对。适配器将一个或多个类接口变成客户端所期望的一个接口;
一个外观也可以只针对一个拥有复杂接口的类提供简化的接口;
两者的差异不在于它们包装了几个类,而在于它们的意图;适配器的意图是,"改变"接口符合客户的期望;而外观的意图是,提供子系统的一个简化接口;

构造家庭影院

package hey.adapter;

  

public class HomeTheaterFacade {

//需要的子系统组件全部在这里
Amplifier
amp;
Tuner
tuner;
DvdPlayer
dvd;
CdPlayer
cd;
Projector
projector;
TheaterLights
lights;
Screen
screen;
PopcornPopper
popper;
//外观将子系统中每一个组件的引用都传入它的构造函数中。然后外观把它们赋值给相应的实例变量
public HomeTheaterFacade(Amplifier
amp,
Tuner
tuner,
DvdPlayer
dvd,
CdPlayer
cd,
Projector
projector,
Screen
screen,
TheaterLights
lights,
PopcornPopper
popper){
this.amp =
amp;
this.tuner =
tuner;
this.dvd =
dvd;
this.cd =
cd;
this.projector =
projector;
this.screen =
screen;
this.lights =
lights;
this.popper =
popper;
}
//该方法将每项任务依次处理。每项任务都是委托子系统中相应的组件处理的
public void watchMovie(String
movie){
System.out.println("Get ready to watch a movie。。。");
popper.on();
popper.pop();
lights.dim(10);
screen.down();
projector.on();
projector.wideScreenMode();
amp.on();
amp.setDvd(dvd);
amp.setSurroundSound();
amp.setVolume(5);
dvd.on();
dvd.play(movie);
}
//该方法负责关闭一切。每项任务也都是委托子系统中合适的组件处理的
public void endMovie(){
System.out.println("Shutting movie theater down。。。");
popper.off();
lights.off();
screen.up();
projector.off();
amp.off();
dvd.stop();
dvd.eject();
dvd.off();
}
}

一众其它类~~~其它~~~

public class TheaterLights {

public void dim(int
i) {
System.out.println("Theater Ceiling Lights dimming to "
+ i +
"%");
}
public void off() {
System.out.println("Theater Ceiling Lights on");
}
}

public class PopcornPopper {

public void on() {
System.out.println("Popcorn Poper on");
}
public void pop() {
System.out.println("Popcorn Popper popping popcorn!");
}
public void off() {
System.out.println("Popcorn Poper off");
}
}

public class Screen {

public void down() {
System.out.println("Theater Screen going down");
}
public void up() {
System.out.println("Theater Screen going up");
}
}

public class Projector {

public void wideScreenMode() {
System.out.println("Top-O-Line Projector in widescreen mode
(16*9 aspect ratio)");
}
public void on() {
System.out.println("Top-O-Line Projector on");
}
public void off() {
System.out.println("Top-O-Line Projector off");
}
}

public class Amplifier {

public void on() {
System.out.println("Top-O-Line Amplifier on");
}
public void setDvd(DvdPlayer
dvd) {
System.out.println("Top-O-Line Amplifier setting DVD player
to Top-O-Line DVD Player");
}
public void setSurroundSound() {
System.out.println("Top-O-Line Amplifier surrond sound on
(5 speakers,1 subwoofer)");
}
public void setVolume(int
i) {
System.out.println("Top-O-Line Amplifier setting volume to
" + i);
}
public void off() {
System.out.println("Top-O-Line Amplifier off");
}
}

public class DvdPlayer {

private String
movie;
public void on() {
System.out.println("Top-O-Line DVD Player on");
}
public void play(String
movie) {
this.movie =
movie;
System.out.println("Top-O-Line DVD Player playing \""
+ movie +
"\"");
}
public void stop() {
System.out.println("Top-O-Line DVD Player stopped \""
+ movie +
"\"");
}
public void eject() {
System.out.println("Top-O-Line DVD Player eject");
}
public void off() {
System.out.println("Top-O-Line DVD Player off");
}
  

}

测试

public class HomeTheaterTestDrive {

public static void main(String[]
args) {
//实例化组件。正常的情况下,某个外观会被指派给客户使用,而不需要由客户自行创建外观
Amplifier
amp =
new Amplifier();
Tuner
tuner =
new Tuner();
DvdPlayer
dvd =
new DvdPlayer();
CdPlayer
cd =
new CdPlayer();
Projector
projector =
new Projector();
TheaterLights
lights =
new TheaterLights();
Screen
screen =
new Screen();
PopcornPopper
popper =
new PopcornPopper();
//根据子系统所有的组件来实例化外观
HomeTheaterFacade
homeTheater =
new HomeTheaterFacade(amp,tuner,dvd,cd,projector,screen,lights,popper);
//使用简化的接口,先开启电影,然后关闭电影
homeTheater.watchMovie("Raiders of the lost Ark");
homeTheater.endMovie();
}
}

结果:



定义外观模式
外观模式的意图是要提供一个简单的接口,好让一个子系统更易于使用;

类图:



"最少知识"原则

该原则告诉我们,要减少对象之间的交互,只留下几个"密友";
设计一个系统,不管是任何对象,都要注意它所交互的类有哪些,并注意它和这些类是如何交互的;
该原则期望我们在设计中,不要让太多的类耦合在一起,以免在修改系统时,影响其它部分;

如何避免这种情况?该原则提出了如下方针:就任何对象而言,在该对象的方法内,我们只应该调用属于以下范围的方法:
该对象本身;
被当作方法的参数而传递进来的对象;

此方法所创建或实例化的任何对象;
对以上三句话的总结就是:如果某对象是调用其它的方法的返回结果,不要调用该对象的方法!

对象的任何组件;(把"组件"想象成是被实例变量所引用的任何对象。换句话说,把该对象想成是"有一个"关系)

为什么该原则会如此苛刻?

如果调用从另一个调用中返回的对象的方法,相当于向另一个对象的子部分发请求(而增加我们直接认识的对象数目)。在这种情况下,原则要我们改为要求该对象为我们做出请求。这样的话,我们就不需要认识该对象的组件了,比如:





将方法限定在界限内
下面的汽车类,展示了调用的方法,同时遵守了最少知识原则:

public class Car {

Engine
engine;//这是一个类的组件,我们可以调用它的方法
public Car(){}
public void start(Key
key){
Doors
doors =
new Doors();//创建一个新对象,需要调用它的方法
boolean
authorized =
key.turns();//被当作参数传递进来的对象,其方法可以被调用
if(authorized){
engine.start();//可以调用对象组件的方法
updateDashboardDisplay();//可以调用同一个对象内的本地方法
doors.lock();//可以调用你所创建或实例化的对象的方法
}
}
private void updateDashboardDisplay() {
//更新显示
}
}

最少知识原则的缺点
采用这个原则会导致更多的"包装"类被制造出来,以处理和其他组件的沟通,这可能导致复杂度和开发时间的增加,并降低运行时的性能;

外观和最少知识原则

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  设计模式