设计模式-外观模式

模式定义

外观模式,又称为门面模式,是一种使用频率非常高的结构型设计模式,是迪米特法则的一种具体实现,它通过引入一个外观角色来简化客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,降低子系统与客户端的耦合度,且客户端调用非常方便。如下图:
外观模式原理

举个例子:如网站导航
外观模式举例

主要作用

  1. 实现客户类与子系统类的松耦合;
  2. 降低原有系统的复杂度;
  3. 提高了客户端使用的便捷性,使得客户端无须关心子系统的工作细节,通过外观角色即可调用相关功能。

UML类图

外观模式

角色

  • 外观角色(Facade):也称 门面角色,系统对外的统一接口;

  • 子系统角色(SubSystem):可以同时有一个或多个 SubSystem。每个 SubSytem 都不是一个单独的类,而是一个类的集合。SubSystem 并不知道 Facade 的存在,对于 SubSystem 而言,Facade 只是另一个客户端而已(即 Facade 对 SubSystem 透明)。

外观模式的目的不是给予子系统添加新的功能接口,而是为了让外部减少与子系统内多个模块的交互,松散耦合,从而让外部能够更简单地使用子系统。

外观模式的本质是:封装交互,简化调用。

模式优点

  1. 降低了客户类与子系统类的耦合度,实现了子系统与客户之间的松耦合关系;

    • 只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类;
    • 减少了与子系统的关联对象,实现了子系统与客户之间的松耦合关系,松耦合使得子系统的组件变化不会影响到它的客户。
  2. 外观模式对客户屏蔽了子系统组件,从而简化了接口,减少了客户处理的对象数目并使子系统的使用更加简单。

    • 引入外观角色之后,用户只需要与外观角色交互;
    • 用户与子系统之间的复杂逻辑关系由外观角色来实现。
  3. 降低原有系统的复杂度和系统中的编译依赖性,并简化了系统在不同平台之间的移植过程。
    因为编译一个子系统一般不需要编译所有其他的子系统。一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。

模式缺点

  1. 不能很好地限制客户端直接使用子系统类,如果对客户端访问子系统类做太多的限制则减少了可变性和灵活性。

  2. 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。

适用场景

  • 为一个复杂的模块或子系统提供一个简洁的供外界访问的接口;
  • 希望提高子系统的独立性时;
  • 预防代码污染:当子系统由于不可避免的暂时原因导致可能存在 bug 或性能相关问题时,可以通过 外观模式 提供一个高层接口,隔离客户端与子系统的直接交互,方便后续问题修改;
  • 在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。

使用步骤

创建外观角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main.java.com.study.designPatterns.facade.demoOne;

/**
* @author: whb
* @description: 外观角色Facade
*/
public class Facade {
/**
* 子系统A
*/
private SubSystemA a = new SubSystemA();
/**
* 子系统B
*/
private SubSystemB b = new SubSystemB();
/**
* 子系统C
*/
private SubSystemC c = new SubSystemC();

// 对外接口
public void doA() {
this.a.doA();
}

// 对外接口
public void doB() {
this.b.doB();
}

// 对外接口
public void doC() {
this.c.doC();
}
}

创建子系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package main.java.com.study.designPatterns.facade.demoOne;

/**
* @author: whb
* @description: 子系统A
*/
public class SubSystemA {
public void doA() {
System.out.println("处理A系统的业务...");
}
}

package main.java.com.study.designPatterns.facade.demoOne;

/**
* @author: whb
* @description: 子系统B
*/
public class SubSystemB {
public void doB() {
System.out.println("处理B系统的业务...");
}
}


package main.java.com.study.designPatterns.facade.demoOne;

/**
* @author: whb
* @description: 子系统C
*/
public class SubSystemC {
public void doC() {
System.out.println("处理C系统的业务...");
}
}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main.java.com.study.designPatterns.facade.demoOne;

/**
* @author: whb
* @description: 客户端调用
*/
public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.doA();
facade.doB();
facade.doC();
}
}

与适配模式的区别

  • 外观模式的实现核心主要是——由外观类去保存各个子系统的引用,实现由一个统一的外观类去包装多个子系统类,然而客户端只需要引用这个外观类,然后由外观类来调用各个子系统中的方法。

  • 这样的实现方式非常类似适配器模式,然而外观模式与适配器模式不同的是:适配器模式是将一个对象包装起来以改变其接口,而外观是将一群对象 ”包装“起来以简化其接口。它们的意图是不一样的,适配器是将接口转换为不同接口,而外观模式是提供一个统一的接口来简化接口。

本文标题:设计模式-外观模式

文章作者:王洪博

发布时间:2018年09月14日 - 21:09

最后更新:2019年09月12日 - 10:09

原始链接:http://whb1990.github.io/posts/6f592ef9.html

▄︻┻═┳一如果你喜欢这篇文章,请点击下方"打赏"按钮请我喝杯 ☕
0%