设计模式~外观模式

106 阅读4分钟

1. 定义

  • 外观模式要求一个子系统的外部与内部必须通过一个统一的对象进行通信,他定义了一个高层接口,使得子系统更加容易使用。

3. UML类图

image.png

  • Facade:外观类,系统对外的统一接口,将客户端的请求代理给适当的子系统对象
  • SubSystem:子系统类,实现子系统的功能,处理外观类指派的任务,注意子系统类不含有外观类的引用。

4. 使用场景

  • 构建一个有层次结构的子系统时,使用外观模式定义子系统中每层的入口点,如果子系统之间是相互依赖的,则可以让他们通过外观接口进行通信,减少子系统之间的依赖关系。
  • 子系统往往会因为不断的重构演化而变得越来越复杂,大多数的模式使用时也会产生很多很小的类,这给外部调用他们的用户程序带来了使用的困难,我们可以使用外观类提供一个简单的接口,对外隐藏子系统的具体实现并隔离变化。
  • 当维护一个遗留的大型系统时,可能这个系统已经非常难以维护和拓展,但因为它含有重要的功能,新的需求必须依赖于它,则可以使用外观类,来为设计粗糙或者复杂的遗留代码提供一个简单的接口,让新系统和外观类交互,而外观类负责与遗留的代码进行交互。

5. 简单实现

  • 我们使用的手机包含了很多功能,这些功能的使用得益于内部硬件子系统的调用工作,譬如手机可以打电话,调用摄像头拍照等,这时手机就相当于一个外观类,而手机内部的通话系统以及摄像头就充当了子系统类,使用时无需关心内部如何工作,只需操作手机这个外观类即可
  • 定义子系统通话接口
/**
 * 定义通话子系统接口,主要包括拨号和挂断的功能
 *
 * @author BTPJ  2022/12/5
 */
public interface Phone {

    /**
     * 拨号
     */
    void dail();

    /**
     * 挂断
     */
    void hangup();
}
  • 定义子系统通话接口具体实现类
/**
 * 通话子系统具体实现类
 * 
 * @author BTPJ  2022/12/5
 */
public class PhoneImpl implements Phone{
    @Override
    public void dail() {
        System.out.println("拨打电话");
    }

    @Override
    public void hangup() {
        System.out.println("挂断电话");
    }
}
  • 定义摄像头子系统接口,主要包括打开、拍照以及关闭的功能
/**
 * 定义摄像头子系统接口,主要包括打开、拍照以及关闭的功能
 *
 * @author BTPJ  2022/12/5
 */
public interface Camera {

    void open();

    void takePicture();

    void close();
}
  • 摄像头子系统具体实现类
/**
 * 摄像头子系统具体实现类
 *
 * @author BTPJ  2022/12/5
 */
public class CameraImpl implements Camera {
    @Override
    public void open() {
        System.out.println("打开摄像头");
    }

    @Override
    public void takePicture() {
        System.out.println("拍照");
    }

    @Override
    public void close() {
        System.out.println("关闭摄像头");
    }
}
  • 利用手机外观类集成子系统的功能
/**
 * 手机外观类
 *
 * @author BTPJ  2022/12/5
 */
public class Mobile {
    private Phone mPhone = new PhoneImpl();
    private Camera mCamera = new CameraImpl();

    /**
     * 暴露拨打电话的方法
     */
    public void dail() {
        mPhone.dail();
    }

    /**
     * 暴露挂断电话的方法
     */
    public void hangup() {
        mPhone.hangup();
    }

    /**
     * 暴露拍照的方法
     */
    public void takePicture() {
        mCamera.open();
        mCamera.takePicture();
        mCamera.close();
    }

    /**
     * 模拟视频通话
     */
    public void videoChat() {
        System.out.println("------模拟视频通话-----");
        mPhone.dail();
        mCamera.open();
    }
}
  • 客户端调用
/**
 * 客户端调用
 *
 * @author BTPJ  2022/12/5
 */
public class Client {
    public static void main(String[] args) {
        Mobile mobile = new Mobile();
        mobile.takePicture();
        mobile.videoChat();
    }
}

- 运行结果:
打开摄像头
拍照
关闭摄像头
------模拟视频通话-----
拨打电话
打开摄像头

6. 源码中的使用场景

  • ContextImpl外观类集成了ActivityManager、PackageManager、PowerManager等子系统类,从而使得ContextImpl的抽象子类Context拥有了startActivity()、sendBrodcast()、bindService()等方法

7. 优缺点

  • 优点:
    • 对客户程序隐藏内部子系统细节,因而减少了客户对于子系统的耦合,能拥抱变化
    • 外观类对子系统的封装使得系统更易于使用
  • 缺点:
    • 外观类接口变多。由于子系统的接口都由外观类统一对外暴露,使得外观类的接口增多
    • 外观类没有遵循开闭原则,当业务变更时,可能需要直接修改外观类