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

来源:互联网 发布:免费qq软件下载 编辑:程序博客网 时间:2024/05/22 05:28

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

  

知识点的梳理:

  • "最少知识"原则:只和你的密友谈话;
  • 当需要简化并统一一个很大的接口或者一群复杂的接口时,使用外观;
  • 外观将客户从一个复杂的子系统中解耦;
  • 实现一个外观,需要将子系统组合进外观中,然后将工作委托给子系统执行;
  • 可以为一个子系统实现多个外观;

      

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

  • 开始介绍前的惯例,来个栗子
    • 需求:建立自己的家庭影院:包含DVD播放器,摄影机,自动屏幕,环绕立体声,爆米花机等等;
      • 观看一部电影,我们需要执行如下步骤:
      • 将以上步骤写成类或者方法:
      • 其它问题:
        • 看完电影后难道还要反向把所有步骤来一遍?
        • 如果要听CD也这么麻烦?
        • 如果要升级硬件系统,还要重新学习操作步骤?
    • 引入外观模式
      • 通过外观模式,实现一个提供合理接口的外观类,可以将一个复杂的子系统变得容易使用。
    • 为家庭影院创建一个外观类HomeTheaterFacade的新类,对外暴露出几个简单的方法,例如watchMovie():
      • 这个外观类将家庭影院的诸多组件视为一个子系统,通过调用这个子系统,来实现watchMovie()方法
    • 现在,你的客户代码可以调用此家庭影院外观所提供的方法,而不必再调用这个子系统的方法。所以,想要看电影只要调用方法watchMovie()方法即可。
      • 外观只是提供更直接的操作,并未将原来的子系统阻隔起来。如果需要子系统类的更高层功能,还是可以使用原来的子系统;
    • 一些问题
      • 外观封装了子系统的类,如果需要低层功能的客户如何接触这些类?
        • 外观只是提供了子系统的简化接口,并没有封装子系统。子系统的功能依然暴露在外;
      • 外观是否可以增加新的功能,或者它只是将没一个请求转由子系统执行?
        • 完全可以;
      • 每个子系统只能有一个外观吗?
        • 可以为一个系统创建许多个外观;
      • 除了提供一个比较简单的接口,外观模式还有其它的优点吗?
        • 外观模式也允许你将客户实现从任何子系统中解耦;
        • 比如,你升级家庭影院,采用全新的接口。如果当初的代码是针对外观而不是针对子系统编写的,现在就不需要改变客户代码,只需要修改外观代码就可以了;
      • 适配器模式和外观模式的差异在于:前者包装一个类,后者代表许多类?
        • 不对。适配器将一个或多个类接口变成客户端所期望的一个接口;
        • 一个外观也可以只针对一个拥有复杂接口的类提供简化的接口;
        • 两者的差异不在于它们包装了几个类,而在于它们的意图;适配器的意图是,"改变"接口符合客户的期望;而外观的意图是,提供子系统的一个简化接口;
    • 构造家庭影院

package hey.adapter;

  

public class HomeTheaterFacade {

//需要的子系统组件全部在这里

Amplifieramp;

Tunertuner;

DvdPlayerdvd;

CdPlayercd;

Projectorprojector;

TheaterLightslights;

Screenscreen;

PopcornPopperpopper;

//外观将子系统中每一个组件的引用都传入它的构造函数中。然后外观把它们赋值给相应的实例变量

public HomeTheaterFacade(Amplifieramp,

Tunertuner,

DvdPlayerdvd,

CdPlayercd,

Projectorprojector,

Screenscreen,

TheaterLightslights,

PopcornPopperpopper){

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(Stringmovie){

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(inti) {

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(DvdPlayerdvd) {

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(inti) {

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 Stringmovie;

public void on() {

System.out.println("Top-O-Line DVD Player on");

}

public void play(Stringmovie) {

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) {

//实例化组件。正常的情况下,某个外观会被指派给客户使用,而不需要由客户自行创建外观

Amplifieramp = new Amplifier();

Tunertuner = new Tuner();

DvdPlayerdvd = new DvdPlayer();

CdPlayercd = new CdPlayer();

Projectorprojector = new Projector();

TheaterLightslights = new TheaterLights();

Screenscreen = new Screen();

PopcornPopperpopper = new PopcornPopper();

//根据子系统所有的组件来实例化外观

HomeTheaterFacadehomeTheater = new HomeTheaterFacade(amp,tuner,dvd,cd,projector,screen,lights,popper);

//使用简化的接口,先开启电影,然后关闭电影

homeTheater.watchMovie("Raiders of the lost Ark");

homeTheater.endMovie();

}

}

  • 结果:
  • 定义外观模式
    • 外观模式的意图是要提供一个简单的接口,好让一个子系统更易于使用;
    • 类图:
  • "最少知识"原则
    • 该原则告诉我们,要减少对象之间的交互,只留下几个"密友";
      • 设计一个系统,不管是任何对象,都要注意它所交互的类有哪些,并注意它和这些类是如何交互的;
      • 该原则期望我们在设计中,不要让太多的类耦合在一起,以免在修改系统时,影响其它部分;
    • 如何避免这种情况?该原则提出了如下方针:就任何对象而言,在该对象的方法内,我们只应该调用属于以下范围的方法:
      • 该对象本身;
      • 被当作方法的参数而传递进来的对象;
      • 此方法所创建或实例化的任何对象;
        • 对以上三句话的总结就是:如果某对象是调用其它的方法的返回结果,不要调用该对象的方法!
      • 对象的任何组件;(把"组件"想象成是被实例变量所引用的任何对象。换句话说,把该对象想成是"有一个"关系)
    • 为什么该原则会如此苛刻?
      • 如果调用从另一个调用中返回的对象的方法,相当于向另一个对象的子部分发请求(而增加我们直接认识的对象数目)。在这种情况下,原则要我们改为要求该对象为我们做出请求。这样的话,我们就不需要认识该对象的组件了,比如:
    • 将方法限定在界限内
      • 下面的汽车类,展示了调用的方法,同时遵守了最少知识原则:

public class Car {

Engineengine;//这是一个类的组件,我们可以调用它的方法

public Car(){}

public void start(Keykey){

Doorsdoors = new Doors();//创建一个新对象,需要调用它的方法

booleanauthorized = key.turns();//被当作参数传递进来的对象,其方法可以被调用

if(authorized){

engine.start();//可以调用对象组件的方法

updateDashboardDisplay();//可以调用同一个对象内的本地方法

doors.lock();//可以调用你所创建或实例化的对象的方法

}

}

private void updateDashboardDisplay() {

//更新显示

}

}

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