spice and wolfspice and wolf Be the One you wanna Be

设计模式-外观模式

外观模式的目的是提供一个统一接口,用来访问子系统中的一群接口,以此来让系统更加易用。

在家庭影院盛行的现在,很多家庭都配备了自家的家庭影院,每当我们需要通过家庭影院进行自我放松时,需要做很多准备工作,比如:打开电脑、打开投影仪、将投影和电脑通过hdmi连接、打开外置音箱、连接外置音箱管线、放下幕布、拉上窗帘、关闭室内灯等,而且在观影完毕后也需要将设备还原,这未免也太麻烦了,如果有一种一键开启家庭影院的方式,即使在工作日下班后我们也很乐意享受一番。所以如果我们能将这些操作封装起来,进行一键部署,就能省不少事,这就是外观模式的主要目的。

下面是通过外观模式实现家庭影院的代码,我们可以看看外观模式是在这个例子中给我们提供便利的:

public class Popper {
    public void on() {}

    public void pop() {}

    public void off() {}
}
public class Lights {
    int bright;

    public Lights() {
        this.bright = 100;
    }
    public void dim(int bright) {
        this.bright = bright;
    }
}
public class Screen {
    public void up() {}

    public void down() {}

    public String toString() {
        return "Screen";
    }
}
public class Projector {
    HdmiInput hdmiInput;
    public void on() {}

    public void setInput(HdmiInput in) {
        hdmiInput.hdmi();
    }

    public void wideScreenMode() {}

    public void off() {
    }
}
public class Computer implements HdmiInput {
    @Override
    public void hdmi() {

    }

    public void on() {}

    public void play(String movie) {};

    public void off() {}
}
public class HomeTheaterFacade {
    Popper popper;
    Lights lights;
    Screen screen;
    Projector projector;
    Computer computer;

    public HomeTheaterFacade(Popper popper,
                             Lights lights,
                             Screen screen,
                             Projector projector,
                             Computer computer) {
        this.popper = popper;
        this.lights = lights;
        this.screen = screen;
        this.projector = projector;
        this.computer = computer;
    }

    public void watchMovie(String movie) {
        System.out.println("Get ready to watch a movie");
        popper.on();
        popper.pop();
        lights.dim(10);
        screen.down();
        computer.on();
        projector.on();
        projector.setInput(computer);
        computer.play(movie);
    }

    public void endMovie() {
        System.out.println("Shutting movie theater down...");
        popper.off();
        lights.dim(100);
        screen.up();
        projector.off();
        computer.off();
    }
}
public class HomeTheaterTestDrive {
    public static void main(String[] args) {
        Popper popper = new Popper();
        Lights lights = new Lights();
        Screen screen = new Screen();
        Projector projector = new Projector();
        Computer computer = new Computer();
        projector.setInput(computer);
        HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade(popper, lights, screen, projector, computer);
        homeTheaterFacade.watchMovie("Forest Gump");
        homeTheaterFacade.endMovie();
    }
}

上面代码将家庭影院所需要做的事情全部集中到一个类中,并封装到数个方法中。这就是外观模式的好处,通过一个接单接口访问系统,既不会暴露系统的实现细节,又能让客户更方便的调用。下面是外观模式的类图:

外观模式遵循了一个设计原则,即最少知识原则:只和关系最近的几个类交互。这个设计原则的目的在于减少类之间的耦合,减少系统的复杂度,这样在修改代码时,能最大限度减少维护的代码量。

如何遵守最少知识原则,有以下方针:就任何对象而言,在该对象的方法内,我们只应该调用属于以下范围的方法:

  • 该对象本身
  • 被当作方法的参数传递进来的对象
  • 此方法所创建或实例化的任何对象
  • 对象的任何组件

违反上面原则的坏处在于,如果我们这样做,相当于向另一个对象的子部分发送请求。在这种情况下,默认要求我们需要对该对象的内部细节有一定的了解,而且当对象内代码修改后,也会影响到当前请求的调用,但是这并不是客户需要做的,所以以上方针能帮我们增强类方法的透明度,让客户不需要了解类内的实现细节,而且还能减少耦合,降低维护成本。

举个例子:

public float getTemp() {
    // 从气象站取得温度计对象
    Thermometer thermometer = station.getThermometer();
    // 从温度计对象取得温度
    return thermometer.getTemperature();
}

这段代码违反了上面的方针,因为温度计对象并不是当前对象的组件,是从station对象中取出的内部对象,所以使用温度计对象违反了以上原则。

改进方案:

public float getTemp() {
    // 从气象站取得温度
    return station.getTemperature();
}

这里我们在气象中中加入了取出气象站温度的方法,所以这里是符合最少知识原则的。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

Press ESC to close