外观模式

外观模式

需求背景

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

模型组成

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

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

迪米特法则

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

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

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

使用案例

  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();
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1.初识外观模式 为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系...
    王侦阅读 5,381评论 0 0
  • 1 场景问题# 1.1 生活中的示例## 外观模式在现实生活中的示例很多,比如:组装电脑,通常会有两种方案。 一个...
    七寸知架构阅读 11,445评论 7 57
  • 【学习难度:★☆☆☆☆,使用频率:★★★★★】直接出处:外观模式梳理和学习:https://github.com/...
    BruceOuyang阅读 4,014评论 0 0
  • 目录 本文的结构如下: 什么是外观模式 模式的结构 代码示例 优点和缺点 适用环境 模式应用 模式扩展 补充 一、...
    w1992wishes阅读 3,808评论 0 1
  • 你还在为别人给你两个馒头而沾沾自喜,却不知别人吞了你的两块金子。 AB两人是同一个村子的,村子拆迁后,逢年过节,原...
    憨憨看戏阅读 2,444评论 0 1