门面模式

定义

门面模式( Facade Pattern) 也叫做外观模式, 是一种比较常用的封装模式, 其定义如下:

Provide a unified interface to a set of interfaces in a subsystem.Facade defines a higher-level interface that makes the subsystem easier to use.( 要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。 门面模式提供一个高层次的接口, 使得子系统更易于使用。 )

角色

  • Facade 门面角色

    这个角色没有没有实际的业务, 而是一个委托类, 客户端调用这个此角色的方法, 此角色会将客户端的请求委派给相应的的子系统中, 客户端不需要在意子系统的实现细节.

  • subSystem子系统角色

    一个门面可以拥有一个或者多个子系统, 一个子系统不是单独的一个类, 而是一个类的集合. 子系统并不知道门面的存在, 对于子系统来说, 门面只是另一个客户端而已.

实例 - 我要写情书

我们要用最浪漫最原始的方式去表白, 写一份情书!

写情书过程的接口和实现

定义写信过程的借口:

public interface ILetterProcess {
    // 写信的内容
    public void writeContext(String context);

    // 写信封
    public void fillEnvelope(String address);

    // 把信放到信封
    public void letterIntoEnvelope();

    // 邮递
    public void sendLetter();
}

实现写信的过程

public class LetterProcessImpl implements ILetterProcess {
    @Override
    public void writeContext(String context) {
        System.out.println("填写信的内容... " + context);
    }

    @Override
    public void fillEnvelope(String address) {
        System.out.println("填写收件人地址及姓名... " + address);

    }

    @Override
    public void letterIntoEnvelope() {
        System.out.println("把信放到信箱中...");
    }

    @Override
    public void sendLetter() {
        System.out.println("邮递信件... ");
    }
}

开始告白

为了告白, 我们需要四个步骤, 对应写信接口的四个方法, 而且这个四个方法是需要按照顺序的, 不能颠倒:

public class Client {
    public static void main(String[] args) {
        ILetterProcess process = new LetterProcessImpl();

        process.writeContext("你好, 我喜欢你!");
        process.fillEnvelope("宁波市江北区风华路201号宁波工程学院, xb");
        process.letterIntoEnvelope();
        process.sendLetter();
    }
}

运行结果:

填写信的内容... 你好, 我喜欢你!
填写收件人地址及姓名... 宁波市江北区风华路201号宁波工程学院, xb
把信放到信箱中...
邮递信件...

但是这样会有一点问题, 首先我们必须要知道它们的顺序, 如果顺序出现错误, 则达不到预期的目标, 这样的写法不符合迪米特法则和接口隔离原则. 而且, 如果要同时要和很多女孩子表白(雾, 这是什么操作)的话, 每次都要这样, 累的要死. 好在现在邮局推出一个代写情书的服务, 只要提供信的内容和地址收信人就行了, 其他的步骤邮局会完成的(雾, 告白能不能有点诚意), 非常好:

public class Facade {
    private ILetterProcess process = new LetterProcessImpl();

    public void sendLetter(String context, String address) {
        process.writeContext(context);
        process.fillEnvelope(address);
        process.letterIntoEnvelope();
        process.sendLetter();
    }
}

有了这种人性化的服务, 我们就能更方便的写信了:

Facade facade = new Facade();
String context = "你好, 我喜欢你!";
String address = "宁波市江北区风华路201号宁波工程学院, xb";
facade.sendLetter(context, address);

结果是和上面一致的, 但是更加简化了, 我们只需要提供内容和地址就行了, 其他什么都不用去管, 而且易于扩展.

现在, 邮局需要核查信件是否安全, 需要进行审查, 所以我们需要对Facade进行扩展:

public class Police {
    public void checkLetter(ILetterProcess process) {
        System.out.println(process + " 信件已经审查...");
    }
}
public class Facade {
    private ILetterProcess process = new LetterProcessImpl();
    private Police police = new Police();
    public void sendLetter(String context, String address) {
        process.writeContext(context);
        process.fillEnvelope(address);
        process.letterIntoEnvelope();
        police.checkLetter(process);
        process.sendLetter();
    }
}

然后再执行一次Client:

填写信的内容... 你好, 我喜欢你!
填写收件人地址及姓名... 宁波市江北区风华路201号宁波工程学院, xb
把信放到信箱中...
io.ride.facade_pattern.LetterProcessImpl@61bbe9ba 信件已经审查...
邮递信件... 

Client程序并没有做出任何修改, 但是可以看到已经增加了审查信件的步骤.

门面模式的优点

  • 减少系统的相互依赖

    门面模式里所有的依赖都是对门面对象的依赖, 与子系统没有关系

  • 提高了灵活性

    不管子系统内部如何变化, 只要不影响到门面对象,对客户端来说就是没有变化. 灵活性提高了.

  • 提高安全性

    想要访问子系统的业务系统, 必须通过门面系统来访问, 否则无法访问.

门面系统的缺点

不管子系统内部如何变化, 只要不影响到门面对象,如果门面对象出现错误,那么只有修改门面角色的代码.

使用场景

  • 为一个复杂的模块或子系统提供一个供外界访问的接口
  • 子系统相对独立——外界对子系统的访问只要黑箱操作即可
  • 预防低水平人员带来的风险扩散

参考资料

  1. 设计模式之禅

ride
167 声望1 粉丝