likes
comments
collection
share

外观模式 Facade

作者站长头像
站长
· 阅读数 27

外观模式 Facade

外观模式(Facade):也称门面模式,为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统在客户端使用的时候更加容易使用。

The Facade Design Patternis a structural design pattern that provides a unified interface to a set of interfaces in a subsystem.

The facade defines a higher-level interface that makes the subsystem easier to use.

外观模式 Facade

外观模式角色很简单:

Subsystem(子系统):如SubsystemASubsystemB, 和 SubsystemC 是子系统的各个组件,它们包含了具体的功能实现,但是对客户端是不可见的。

Facade (外观对象),它提供了一个简单的接口给客户端,隐藏了子系统的复杂性。它包含了对子系统中各个接口的引用,并且提供了简化的方法来实现客户端需求。

上面 UML 类图拿适配器模式来类比的话,Facade 既是是想要转换成为的接口,也是转换 Subsystem 的适配器,确实相差不大。

确定相差不大吗?下面看一下具体实现。

基本实现

子系统 Subsystem

每个子系统都可以被客户端直接调用,或者被门面角色调用。

▪ 子系统A SubsystemA

public class SubsystemA {

    public void methodA(){
        System.out.println("调用子系统A的方法A");
    }
}

▪ 子系统B SubsystemB

public class SubsystemB {

    public void methodB(){
        System.out.println("调用子系统B的方法B");
    }
}

▪ 子系统C SubsystemC

public class SubsystemC {

    public void methodC(){
        System.out.println("调用子系统C的方法C");
    }
}

这里需要注意的是,虽然子系统类都叫 Subsystem 什么什么的,但它们相互之间并不要求有任何关联,它们也都不是同一个接口的实现,它们各自的方法也不要求相互关联。

外观 Facade

外观类,它需要了解所有子系统的方法或属性,进行组合,以被客户端调用。

public class Facade {

    private SubsystemA subsystemA;
    private SubsystemB subsystemB;
    private SubsystemC subsystemC;

    public Facade(){
        this.subsystemA = new SubsystemA();
        this.subsystemB = new SubsystemB();
        this.subsystemC = new SubsystemC();
    }

    public  void facadeMethod(){
        subsystemA.methodA();
        subsystemB.methodB();
        subsystemC.methodC();
    }
}

客户端 CLient

Facade 类中的 facadeMethod() 方法是一个统一的接口,它可以调用子系统中的多个方法来完成一个任务。

Facade facade = new Facade();
facade.facadeMethod();

输出结果如下:

调用子系统A的方法A
调用子系统B的方法B
调用子系统C的方法C

上面的基本代码实现是不是很眼熟,很像 Controller - Service - Dao 三层模型,系统的代码按照功能划分为三个层次,每个层次承担不同的责任和功能,请求只和 Controller 层打交道,ControllerService 的门面,ServiceDao 的门面。

其实设计模式是为了解决某一特定问题而出现的,不能为了设计而设计,下面看一下门面模式在源码中的运用,也许会对设计模式有更深的体会。

源码鉴赏

Tomcat 之 RequestFacade

外观模式 Facade

Tomcat 作为 WEB 容器,可以将客户端发送过来的请求封装成 ServletRequest,对于 HTTP 请求则是封装成 HttpServletRequest 的子类 Request。

这个 Request 作为请求的完整封装,是给 Tomcat 内部使用的,如果将其交给用户自定义的 HttpServlet 会很危险,所以就搞了一个门面将其包装了起来,只暴露有限的方法给客户使用。

用户可以通过继承 HttpServlet 自定义 Servlet 处理请求:

public class ServletDemo extends HttpServlet {
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res) 
        throws ServletException, IOException {}
 
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException {}
 
}

方法入参 HttpServletRequest 其实是 RequestFacade。同理,给客户使用的 HttpServletResponse 是 ResponseFacade。

这么说的话,门面、门面 不就是个包装嘛,代理也是包装、装饰者也是包装、适配器也是包装....

把思想放开之后,会发现到处都是都是门面模式,准确的说是包装模式,只要能解决问题,方便维护,即使用不规范的设计模式,甚至不用设计模式,也没什么大不了的。

总结

一句话总结,门面模式的目的是让子系统更加易用!

其实门面模式在维护老系统时很好用,老系统不想改但还想用它原本的方法,那就是开发一个门面类,将老系统复杂的逻辑封装起来,让新系统只与门面交互。

门面模式优点如下:

简化接口:  门面模式提供了一个统一的接口,隐藏了系统的复杂性,使客户端能够更容易地与系统交互。

降低耦合度:  客户端不需要了解系统的内部实现细节,只需要与门面对象进行交互,从而降低了客户端和系统之间的耦合度,这其实也符合迪米特原则。

提高可维护性:  将系统的复杂性封装在门面对象中,使系统更易于维护和修改。

门面模式缺点如下:

不符合开闭原则:  如果系统需要新增功能,可能需要修改门面类,违反了开闭原则。

可能引入性能问题:  如果门面对象负责过多的子系统交互,可能会引入性能问题,导致系统性能下降。

隐藏系统复杂性:  虽然门面模式隐藏了系统的复杂性,但有时也可能导致客户端对系统整体的理解不足,造成误解或不当使用。

综上所述,门面模式在简化接口、降低耦合度等方面有着明显的优势,但也需要注意可能存在的性能问题和对系统整体的理解。

转载自:https://juejin.cn/post/7373986431850905637
评论
请登录