外观模式

作者: GeekerLou | 来源:发表于2020-03-08 10:27 被阅读0次

    外观模式

    需求背景

    外观就是这个一键启动的按钮,它将多个模块或系统的代码进行了整合,而我们只要简单地调用外观暴露出来的一个接口。这就是外观模式( 也叫门面模式 ),其作用显而易见,就是提供一个简单接口来调用后方一群复杂的接口。

    模型组成

    在外观模式中主要有三个角色:

    • 子系统:已有模块或子系统,提供了一系列复杂的接口或功能。
    • 外观( 门面 ):它了解子系统,并对外暴露一个简单的接口。
    • 客户:调用外观提供的接口来实现功能,无需了解复杂的子系统。

    迪米特法则

    迪米特法则是说每一个类都应该尽量地少知道别的类,外观模式就是迪米特法则的应用。原本我们需要知道许多的子系统或接口,用了外观类之后,我们仅仅需要知道外观类即可。

    换句话说就是:知道的太多对你没好处。

    迪米特法则是希望类之间减少耦合,类越独立越好。有句话叫牵一发而动全身,如果类之间关系太紧密,与之关联的类太多,一旦你修改该类,也许会动到无数与之关联的类。

    使用案例

    1. JAVA 三层结构

    用 JAVA 开发我们经常使用三层结构:

    • controller 控制器层。
    • Service 服务层。
    • Dao 数据访问层。
    //转账
    public boolean transMoney(Integer user1,Integer user2,Float money){
        //用户1加钱
        userDao.addMoney(user1,money);
        //用户2扣钱
        userDao.decMoney(user2,money);
        //转账日志
        logDao.addLog(user1,user2,money);
    }
    

    作为调用方来说,并不想知道转账操作具体要调用哪些 Dao,一行代码 transMoney() 就能搞定岂不是皆大欢喜。

    因此 Service 是很有必要的,一般在业务系统中,Service 层的类不仅仅是简单的调用 Dao,而是作为外观,给 Controller 提供了更方便好用的接口。

    不过无论多复杂的系统,总会有 Service 直接调用 Dao 的 getUserById() 的情况 ,我们是否可以偷懒直接在 Controller 调用 Dao 呢?理论上是没问题的,但是强烈建议不要这么干,因为这样会导致层侵入,三层结构的层级混乱。

    除非你的业务真的简单到极致,那么干脆直接舍弃 Service 层。只要你有Service 层,就请不要跨层调用。

    1. Tomcat 中的外观模式

    RequestFacade 源码中可以看到,当调用 getAttribute() , getProtocol() 等方法时,其实还是调用了 Request 对象的 getAttribute() 方法。

    既然如此,为什么要多此一举弄个 RequestFacade 呢 ,其实是为了安全,Tomcat 不想把过多的方法暴露给别人。

    外观模式不仅仅用于将复杂的接口包装为一个简单的接口,也可以用于隐藏一些不想暴露给别人的方法或接口。

    使用场景

    • 包装多个复杂的子系统,提供一个简单的接口。
    • 重新包装系统,隐藏不想暴露的接口。

    优缺点比较

    1. 优点
    • 将复杂的接口简单化,减少了客户端与接口之间的耦合,提高了安全性。
    1. 缺点
    • 可能产生大量的中间类( 外观类 ),一定程度上增加了系统的复杂度。

    示例

    假设我们有一台计算器,在启动计算机的时候我们需要依次启动它的CPU、内存和磁盘,但是对于外部用户在使用的时候,如果我们直接暴露上述功能给用户,一个是增加了用户的学习和使用成本,二个是增大的出现错误和引起故障的风险,对于这种本就是内部业务处理的范畴,应该将其封装后暴露一个简易的入口出来,比如一个启动按钮。

    下面我们通过程序模拟一下这个过程:
    CPU类:

    public class CPU {
        public void start(){
            System.out.println("启动CPU");
        }
    }
    

    Memory类:

    public class Memory {
        public void start(){
            System.out.println("启动内存");
        }
    }
    

    Disk类:

    public class Disk {
        public void start(){
            System.out.println("启动硬盘");
        }
    }
    

    启动按钮类:

    public class StartBtn {
        public void start() {
            new CPU().start();
            new Disk().start();
            new Memory().start();
        }
    }
    

    单元测试

    import com.netease.learn.designPattern.facade.StartBtn;
    import org.junit.Test;
    
    public class FacadeTest {
    
        @Test
        public void test() {
            new StartBtn().start();
        }
    }
    

    相关文章

      网友评论

        本文标题:外观模式

        本文链接:https://www.haomeiwen.com/subject/kjxddhtx.html