设计模式之外观模式

简介: 设计模式之外观模式

一、介绍

外观模式(Facade Pattern),又称为门面模式,属于结构型设计模式。外观模式为一组子系统逻辑调用提供一个独立的接口调用。

在不使用外观模式的情况下,实现一个功能时需要按照一定的顺序对多个方法调用,因此我们需要关注对哪些方法进行调用,以及按照什么样的顺序。

而在外观模式中,我们通过调用外观类(Facade)的一个方法就可以实现该功能,该外观类(Facade)内部按照对应的顺序对这些方法进行调用,而具体按照什么样的顺序调用哪些方法我们无需关注,交给外观类(Facade)就可以了。

为子系统中的一组接口提供一个一致的界面。Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

外观模式将我们的关注点从对子系统的调用转移到外观类(Facade)上

外观模式可以隐藏某个功能的内部逻辑细节,而提供给我们最简单的方式实现该功能。

二、一个形象的比喻

我是房地产开发商,我想盖一栋大楼,但是盖大楼需要各种各样的建筑商(搞地基的,砌墙的,搞水泥的,等等),我是高贵的开发商,我不想亲力亲为地联系这么多建筑商,怎么办呢?哎~,我找个包工头,我只和包工头对接大楼的进展,至于找哪家建筑商怎么找,这是包工头的事,I don't care,我只关注这栋大楼能盖好就行。

三、外观模式中的角色

在外观模式中,包含以下三个角色:

  • 子系统(System)

    功能的实际提供者,多个子系统共同实现一个功能。

  • 外观类(Facade)

    将多个子系统的功能进行整合,并向客户端提供实现整合后功能的唯一接口。用于隐藏功能的内部细节。

  • 调用方(Client)

    外观类的调用方,只需要关注外观类提供的功能,无需关注内部细节。

下面是外观模式的UML类图

UML图.png

四、案例引用

我本人从上小学至今特别喜欢吃西红柿炒鸡蛋面条,记得是小学四年级的时候,我一顿午饭吃了一大海碗,吃得干干净净一点不浪费,那就以制作一顿西红柿炒鸡蛋面条为例吧。

大海碗.jpg

制作一碗西红柿炒鸡蛋面条挺简单的,我们假设现在有三个角色:

  • 负责备菜的小绿

    你负责买菜,回来还要洗菜,并切好西红柿打好鸡蛋

  • 负责西红柿炒鸡蛋的小红

    小绿备菜完成后,该小红上场表演了,很简单,炒鸡蛋炒西红柿加入调味料加入鸡蛋搅拌关火

  • 负责煮面条的小白

    小白再拿出一个汤锅,盛满水,将水烧开加入面条,煮沸两次后捞出面条盛入碗中。

最后舀一些西红柿炒鸡蛋搅拌到面条中,一碗美味的西红柿炒鸡蛋面条就ok了。

在享用完这一顿美食后,还需要有人擦桌子,有人刷锅洗碗。现在我们把这两个工作继续分配

  • 负责备菜的小绿被分配擦桌子
  • 负责煮面条的小白被分配刷锅洗碗

下面我们用代码模拟这个过程。

五、不使用外观模式

在不使用外观模式的情况下,我们客户端通常会在一个方法中先获取小绿、小红、小白这三个角色的实例,然后依次调用这三个角色的方法完成响应的步骤。

1. 子系统A

在前面的介绍中,小绿负责的工作不仅有备菜,还有擦桌子。下面我们对小绿这个角色进行实现

public class XiaoLv {
   
   

    /**  买菜 */
    public void maiCai() {
   
   
        System.out.println("小绿:买菜");
    }
    /**  洗菜 */
    public void xiCai() {
   
   
        System.out.println("小绿:洗菜");
    }
    /**  切西红柿 */
    public void qieXiHongShi() {
   
   
        System.out.println("小绿:切西红柿");
    }
    /**  打鸡蛋 */
    public void daJiDan() {
   
   
        System.out.println("小绿:打鸡蛋");
    }
    /**  擦桌子 */
    public void caZhuoZi() {
   
   
        System.out.println("小绿:擦桌子");
    }
}

2. 子系统B

小绿负责的工作:炒鸡蛋炒西红柿加入调味料加入鸡蛋搅拌关火。下面我们对小红这个角色进行实现

public class XiaoHong {
   
   

    /**  炒鸡蛋 */
    public void chaoJiDan() {
   
   
        System.out.println("小红:炒鸡蛋");
    }
    /**  炒西红柿 */
    public void chaoXiHongShi() {
   
   
        System.out.println("小红:炒西红柿");
    }
    /**  添加调味料 */
    public void addTiaoWeiLiao() {
   
   
        System.out.println("小红:添加调味料");
    }
    /**  加入鸡蛋 */
    public void addJiDan() {
   
   
        System.out.println("小红:加入鸡蛋");
    }
    /**  搅拌 */
    public void jiaoBan() {
   
   
        System.out.println("小红:搅拌");
    }
    /**  关火 */
    public void closeFire() {
   
   
        System.out.println("小红:关火");
    }
}

3. 子系统C

小白负责的工作不仅有煮面条,还有刷锅洗碗。下面我们对小白这个角色进行实现

public class XiaoBai {
   
   

    /**  烧开水 */
    public void shaoKaiShui() {
   
   
        System.out.println("小白:烧开水");
    }
    /**  加入面条 */
    public void addMianTiao() {
   
   
        System.out.println("小白:加入面条");
    }
    /**  捞出面条 */
    public void takeMianTiao() {
   
   
        System.out.println("小白:捞出面条");
    }
    /**  洗碗 */
    public void xiWan() {
   
   
        System.out.println("小白:洗碗");
    }
}

完成上面三个角色后,我们新建一个客户端类FacadeClient来完成这一整个步骤逻辑

4. 客户端类

在客户端Client中,要想制作一碗面条是相当麻烦的,不仅要对三个角色进行实例化,还得时刻关注每一个角色干嘛干嘛,缺少任何一个步骤都可能导致吃不上这顿饭。

public class Client {
   
   

    public static void main(String[] args) {
   
   
        // 获取小绿、小红、小白三个角色的实例
        XiaoLv xiaoLv = new XiaoLv();
        XiaoHong xiaoHong = new XiaoHong();
        XiaoBai xiaoBai = new XiaoBai();

        // 开始做饭
        System.out.println("开始.......");
        System.out.println();
        xiaoLv.maiCai(); // 小绿:买菜
        xiaoLv.xiCai(); // 小绿:洗菜
        xiaoLv.qieXiHongShi(); // 小绿:切西红柿
        xiaoLv.daJiDan(); // 小绿:打鸡蛋
        System.out.println();

        xiaoHong.chaoJiDan(); // 小红:炒鸡蛋
        xiaoHong.chaoXiHongShi(); // 小红:炒西红柿
        xiaoHong.addTiaoWeiLiao(); // 小红:加入调味料
        xiaoHong.addJiDan(); // 小红:加入鸡蛋
        xiaoHong.jiaoBan(); // 小红:搅拌
        xiaoHong.closeFire(); // 小红:关火
        System.out.println();

        xiaoBai.shaoKaiShui(); // 小白:烧开水
        xiaoBai.addMianTiao(); // 小白:加入面条
        xiaoBai.takeMianTiao(); // 小白:捞出面条
        System.out.println();

        System.out.println("开始享用美食:西红柿炒鸡蛋捞面条......");
        System.out.println();

        xiaoLv.caZhuoZi(); // 小白:小绿:擦桌子
        xiaoBai.xiWan(); // 小白:刷锅洗碗
    }
}

运行客户端代码后输出如下

输出结果.jpg

从结果上来看,我的功能需求实现了,做好了饭,吃饱了面,擦好了桌子

从过程上来看,明明我不负责炒菜、煮面等操作,但我却还要花费精力在这上面,其实我根本不关心面是怎么做的,我只在乎我能不能吃到这碗面。

该示例代码的UML图如下所示

不使用外观模式的UML图.png

六、使用外观模式

使用外观模式时,需要新建一个外观类,将做面条这些过程细节都放在外观类里面,外观类只需要给客户端提供一个能返回结果的方法即可。现在比如我去一家河南面馆吃饭,点了一大碗西红柿炒鸡蛋面条,然后坐下等着上饭就行了,饭店可以帮我做好一切事情。

现在我们对上面不使用外观模式的代码进行改进。

1. 外观类

下面新建一个外观类河南面馆(HenanNoodlesFacade)

public class HenanNoodlesFacade {
   
   

    private final XiaoLv xiaoLv = new XiaoLv();
    private final XiaoHong xiaoHong = new XiaoHong();
    private final XiaoBai xiaoBai = new XiaoBai();

    /** 做面条  */
    public void makeNoodles() {
   
   
        xiaoLv.maiCai(); // 小绿:买菜
        xiaoLv.xiCai(); // 小绿:洗菜
        xiaoLv.qieXiHongShi(); // 小绿:切西红柿
        xiaoLv.daJiDan(); // 小绿:打鸡蛋
        System.out.println();

        xiaoHong.chaoJiDan(); // 小红:炒鸡蛋
        xiaoHong.chaoXiHongShi(); // 小红:炒西红柿
        xiaoHong.addTiaoWeiLiao(); // 小红:加入调味料
        xiaoHong.addJiDan(); // 小红:加入鸡蛋
        xiaoHong.jiaoBan(); // 小红:搅拌
        xiaoHong.closeFire(); // 小红:关火
        System.out.println();

        xiaoBai.shaoKaiShui(); // 小白:烧开水
        xiaoBai.addMianTiao(); // 小白:加入面条
        xiaoBai.takeMianTiao(); // 小白:捞出面条
    }

    /** 善后工作 */
    public void clean() {
   
   
        xiaoLv.caZhuoZi(); // 小白:小绿:擦桌子
        xiaoBai.xiWan(); // 小白:刷锅洗碗
    }
}

然后我们想吃面条的时候,只需要和面馆外观类打交道就可以了

2. 客户端类

下面是我们的客户端类FacadeClient

public class FacadeClient {
   
   

    public static void main(String[] args) {
   
   

        HenanNoodlesFacade facade = new HenanNoodlesFacade();
        System.out.println("开始.......");
        System.out.println();

        // 做面条
        facade.makeNoodles();
        System.out.println();

        System.out.println("开始享用美食:西红柿炒鸡蛋捞面条......");
        System.out.println();
        // 善后清理工作
        facade.clean();
    }
}

输出结果如下所示

输出结果.jpg

该示例代码的UML图如下所示

使用外观模式的UML图.png




以上就是对外观模式的介绍与讲解,下面我们对外观模式的优缺点进行阐述。

七、优缺点

优点:

  • 隐藏了功能细节,关注功能本身

缺点:

  • 引入了多余的外观类。因为没有外观类,同样可以实现一样的结果。




纸上得来终觉浅,绝知此事要躬行。

————————我是万万岁,我们下期再见————————

相关文章
|
1月前
|
设计模式 存储 uml
C++ 设计模式实战:外观模式和访问者模式的结合使用,派生类访问基类的私有子系统
C++ 设计模式实战:外观模式和访问者模式的结合使用,派生类访问基类的私有子系统
29 1
|
6月前
|
设计模式 uml
结构型设计模式03-外观模式
结构型设计模式03-外观模式
20 0
|
3月前
|
设计模式
设计模式-外观模式
设计模式-外观模式
31 0
|
3月前
|
设计模式 Go 开发工具
Golang设计模式——03外观模式
Golang设计模式——03外观模式
21 0
|
3月前
|
设计模式 Java 应用服务中间件
设计模式 -结构型模式_门面模式(外观模式) Facade Pattern 在开源软件中的应用
设计模式 -结构型模式_门面模式(外观模式) Facade Pattern 在开源软件中的应用
31 1
|
4月前
|
设计模式 前端开发 Java
Java设计模式【十一】:外观模式
Java设计模式【十一】:外观模式
22 0
|
1月前
|
设计模式 应用服务中间件 智能硬件
【设计模式】外观模式
【设计模式】外观模式
|
6月前
|
设计模式 Java 数据库连接
【设计模式——学习笔记】23种设计模式——外观模式Facade(原理讲解+应用场景介绍+案例介绍+Java代码实现)
【设计模式——学习笔记】23种设计模式——外观模式Facade(原理讲解+应用场景介绍+案例介绍+Java代码实现)
49 0
|
3月前
|
设计模式 Java
聊聊Java设计模式-外观模式
外观(Facade)模式,又叫做门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问的模式。
35 1
聊聊Java设计模式-外观模式
|
3月前
|
设计模式 前端开发 数据库
【设计模式】之外观模式
外观模式是一种简化复杂系统接口的设计模式,在前端开发中有着广泛的应用。它可以帮助我们封装复杂的子系统,并提供一个简单易用的接口给客户端。通过使用外观模式,我们可以提高代码的可维护性、可读性和扩展性。但是需要注意避免过度使用外观模式,以免造成不必要的性能问题。
41 1