外观模式(门面模式)

2019/10/15 posted in  算法

外观模式介绍

外观模式(Facade)在开发过程中的运用频率非常高,尤其是在现阶段各种第三方SDK充斥在我们的周边,而这些SDK很大概率会使用外观模式。通过一个外观类使得整个系统的接口只有一个统一的高层接口,这样能够降低用户的使用成本,也对用户屏蔽了很多实现细节。当然,在我们的开发过程中,外观模式也是我们封装API的常用手段,例如网络模块、ImageLoader模块等。

外观模式定义

要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。

外观模式的使用场景

  1. 为一个复杂子系统提供一个简单接口。子系统往往因为不断演化而变得越来越复杂,甚至可能被替换。大多数模式使用时都会产生更多、更小的类,在这使子系统更具可重用性的同时也更容易对子系统进行定制、修改,这种易变性使得隐藏子系统的具体实现变得尤为重要。Facade可以提供一个简单统一的接口,对外隐藏子系统的具体实现、隔离变化。
  2. 当你需要构建一个层次结构的子系统时,使用Facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过Facade接口进行通信,从而简化它们之间的依赖关系。

外观模式的简单示例

生活中使用外观模式的例子非常多,任何一个类似中央调度结构的组织都类似外观模式。

简单模拟一下手机的外观模式实现,首先我们建立一个MobilePhone类

public class MobilePhone {
    private Phone mPhone = new PhoneImpl();
    private Camera mCamera = new SamsungCamera();
    
    public void dail() {
        mPhone.dail();
    }
    
    public void voideoChat() {
        System.out.println("--> 视频聊天接通中");
        mCamera.open();
        mPhone.dail();
    }
    
    public void hangup() {
        mPhone.hangup();
    }
    
    public void takePicture() {
        mCamera.open();
        mCamera.takePicture();
    }
    
    public void closeCamera() {
        mCamera.close();
    }
}

MobilePhone类中含有两个子系统,也就是拨号系统和拍照系统,MobilePhone将这两个系统封装起来,为用户提供一个统一的操作接口,也就是说用户只需要通过MobilePhone这个类就可以操作打电话和拍照这两个功能。用户不需要知道有Phone这个接口以及它的实现类是PhoneImpl,同样不需要知道Camera相关的信息,通过MobilePhone就可以包揽一切。

看看Phone接口和PhoneImpl

public interface Phone {
    //打电话
    public void dail();
    //挂断
    public void hangup();
}

class PhoneImpl implements Phone {
    @Override
    public void dail() {
        System.out.println("打电话");
    }
    
    @Override
    public void hangup() {
        System.out.println("挂断");
    }
}

Camera也是类似的实现

public interface Camera {
    public void open();
    public void takePicture();
    public void close();
}

class SamsungCamera implements Camera {
    @Override
    public void open() {
        System.out.println("打开相机");
    }
    
    @Override
    public void takePicture() {
        System.out.println("拍照");
    }
    
    @Override
    public void close() {
        System.out.println("关闭相机");
    }
}

测试代码

public class Test {
    public static void main(String[] args) {
        MobilePhone nexus6 = new MobilePhone();
        //拍照
        nexus6.takePicture();
        //视频聊天
        nexus6.videoChat();
    }
}

从上述代码中可以看到,外观模式就是统一接口封装。将子系统的逻辑、交互隐藏起来,为用户提供一个高层次的接口,使得系统更加易用,同时也对外隐藏了具体的实现,这样即使具体的子系统发生了变化,用户也不会感知到,因为用户使用的是Facade高层接口,内部的变化对于用户来说并不可见。这样一来就将变化隔离开来,使得系统也更为灵活。

总结

外观模式是一个高频率使用的设计模式,它的精髓就在于封装二字。通过一个高层次结构为用户提供统一的API入口,使得用户通过一个类型就基本能够操作整个系统,这样减少了用户的使用成本,也能够提升系统的灵活性。

优点

  • 对客户程序隐藏子系统细节,因而减少了客户对于系统的耦合,能够拥抱变化
  • 外观类对子系统的接口封装,使得系统更易于使用

缺点

  • 外观类接口膨胀。由于子系统的接口都由外观类统一对外暴露,使得外观类的API接口较多,在一定程度上增加了用户使用成本
  • 外观类没有遵循开闭原则,当业务出现变更时,可能需要直接修改外观类