设计模式之外观模式

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

一、介绍

外观模式(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




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

七、优缺点

优点:

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

缺点:

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




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

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

相关文章
|
6月前
|
设计模式 API 数据安全/隐私保护
探索设计模式的魅力:外观模式简化术-隐藏复杂性,提供简洁接口的设计秘密
外观模式是一种关键的设计模式,旨在通过提供一个简洁的接口来简化复杂子系统的访问。其核心价值在于将复杂的内部实现细节封装起来,仅通过一个统一的外观对象与客户端交互,从而降低了系统的使用难度和耦合度。在软件开发中,外观模式的重要性不言而喻。它不仅能够提高代码的可读性、可维护性和可扩展性,还能促进团队间的协作和沟通。此外,随着业务需求和技术的发展,外观模式能够适应变化,通过修改外观对象来灵活调整客户端与子系统之间的交互方式。总之,外观模式在软件设计中扮演着举足轻重的角色,是构建高效、稳定且易于维护的软件系统的关键
181 1
探索设计模式的魅力:外观模式简化术-隐藏复杂性,提供简洁接口的设计秘密
|
6月前
|
设计模式 Java
23种设计模式,外观模式的概念优缺点以及JAVA代码举例
【4月更文挑战第6天】外观模式(Facade Pattern)是一种使用频率非常高的结构型设计模式,其核心思想是为子系统中的一组接口提供一个一致的界面。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。简而言之,外观模式就是客户端与复杂子系统之间的一个简单而统一的接口
78 3
|
6月前
|
设计模式 存储 uml
C++ 设计模式实战:外观模式和访问者模式的结合使用,派生类访问基类的私有子系统
C++ 设计模式实战:外观模式和访问者模式的结合使用,派生类访问基类的私有子系统
69 1
|
2月前
|
设计模式 Java
Java设计模式-外观模式(11)
Java设计模式-外观模式(11)
|
1月前
|
设计模式 Java
Java设计模式之外观模式
这篇文章详细解释了Java设计模式之外观模式的原理及其应用场景,并通过具体代码示例展示了如何通过外观模式简化子系统的使用。
29 0
|
3月前
|
设计模式 存储 Java
【九】设计模式~~~结构型模式~~~外观模式(Java)
文章详细介绍了外观模式(Facade Pattern),这是一种对象结构型模式,通过引入一个外观类来简化客户端与多个子系统之间的交互,降低系统的耦合度,并提供一个统一的高层接口来使用子系统。通过文件加密模块的实例,展示了外观模式的动机、定义、结构、优点、缺点以及适用场景,并讨论了如何通过引入抽象外观类来提高系统的可扩展性。
【九】设计模式~~~结构型模式~~~外观模式(Java)
|
4月前
|
设计模式 JavaScript 前端开发
js设计模式【详解】—— 外观模式
js设计模式【详解】—— 外观模式
37 2
|
6月前
|
设计模式
设计模式-外观模式
设计模式-外观模式
56 0
|
5月前
|
设计模式 Java
Java设计模式:外观模式之优雅门面(九)
Java设计模式:外观模式之优雅门面(九)
|
5月前
|
设计模式 Java
Java设计模式之外观模式详解
Java设计模式之外观模式详解