【设计模式】02 外观模式

2.1 场景问题

2.1.1 生活中的例子

在现实生活中,我们可能需要组装电脑,这里就有两种方案。

第一个方案:我们自己去购买组装电脑所需的各种配件,自己 DIY (Do It Youself)。这个方案需要对各个配件都比较熟悉,才能选择最合适的配件,还要考虑是否兼容。

第二个方案:去找专业的装机公司,提出自己的需求,然后就可以拿到现成的电脑了,当然这个价格会比自己 DIY 贵,但是节省了许多时间以及精力。

这个装机公司就相当于是 外观模式。 有了它,我们不需要自己去和众多配件的公司打交道,只需要跟装机公司交互就好了,就可获得组装好的电脑。

假如一个系统有多个模块,若采用第一个方案,我们需要知道每个模块的功能,以及如何组装各个模块,相当繁琐,但采用第二个方案,我们不需要知道各个模块实现的细节。

2.1.2 实际应用 – 代码生成

许多公司都有自己的代码生成工具,根据配置来生成代码。这样就不要再自己实现增删改查,而把更多的精力放在业务功能上。

假设每个模块都有基本的三层架构:分为表现层、逻辑层和数据层。

代码生成工具还需要一个配置管理的模块。通过配置来描述,需要生成哪个模块的代码。用代码该如何实现呢?

2.1.3 不同模式的解决方案

此处省略各个模块的生成代码,只展示客户端的使用代码。

public class Client {
	public static void main(String[] args) {
		// 表现层的代码生成
		new Presentation().generate();
		// 实现层的代码生成
		new Impl().generate();
		// 数据层的代码生成
		new Dao().generate();
	}
}

从上述代码可以看出,在客户端需要与各个模块都有联系,假如一个模块发生改变,客户端也会受到影响。

怎么才能实现客户端能简单的使用各个模块的功能,而又不用客户端去与各个模块交互呢?

2.2 解决方案

2.2.1 使用外观模式来解决问题

  1. 外观模式的定义

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

根据客户端的需要定义一个简单的接口,让客户端调用这个接口,剩下的事情客户端就不用管它,这样客户端就变得简单了。

外观模式就是通过引入这么一个外观类,在这个类里面定义客户端想要的简单的方法,然后在这些方法里面实现。

2.2.2 外观模式示例代码

假设子系统内有三个模块,分别是 AModule、BModule 和 CMoudule。

  1. A 模块
/**
* A 模块的接口
*/
public interface AMoudleApi() {
	/** 
	* 示意方法,A 模块对外的一个方法
	*/
	public void testA() {
	}
}

/**
* A 模块的实现
*/	
public class AModuleImpl implements AModuleApi() {
	public void testA() {
		System.out.println("现在在 A 模块操作 testA 方法");
	}
}
  1. B 模块和 C 模块与 A 模块一样
...
  1. 定义外观对象
/**
* 外观对象
*/
public class Facade {
	/**
	* 示意方法,满足客户的需要
	*/ 
	public void test() {
		// 在内部的实现,可能会调用到内部的多个模块
		AModuleApi a = new AModuleImpl();
		a.testA();
		BModuleApi b = new BModuleImpl();
		b.testB();
		CModuleApi c = new CModuleImpl();
		c.testC();
	}
}
  1. 客户端
public class Client {
	public static void main(String[] args) {
		// 使用 Facade
		new Facade().test();
	}
}

运行结果如下:

现在在 A 模块里面操作 testA 方法
现在在 B 模块里面操作 testB 方法
现在在 C 模块里面操作 testC 方法

3.3 模式讲解

3.3.1 认识外观模式

  1. 外观模式的目的

让外部减少与子系统内的多个模块的交互,松散耦合,从而让外部能够更简单地使用子系统。

  1. 使用外观和不使用外观相比有何变化

如果 Facade 在系统这边,就相当于屏蔽了外部客户端和系统内部模块地交互,不但方便了客户端地调用,而且封装了系统内部的细节实现。

Facade 可以被多个客户端所调用,Facade 就可以实现复用。

  1. 有外观,但是也可以不使用

可以不使用 Facade,直接调用某个具体模块的接口。

  1. 外观提供了缺省的功能实现

外观可以为用户提供一个简单的、缺省的实现。

3.3.2 外观模式的实现

  1. Facade 的实现

直接把外观中的方法实现成为静态的方法,这样就不需要创建外观的对象而直接调用。

public class Facade {
	public static void test() {
		AModuleApi a = new AModuleImpl();
		a.testA();
		BModuleApi b = new BModuleImpl();
		b.testB();
		CModuleApi c = new CModuleImpl();
		c.testC();
	}
}
  1. Facade 可以实现成为 interface

需要提供 Facade 的实现和工厂类

  1. Facade 实现成为 interface 的附带好处

有选择性地暴露接口地方法,尽量减少模块对子系统外提供地接口方法。

3.3.3 外观模式地优缺点

优点:

  • 松散耦合:外观模式松散了客户端与子系统地耦合关系,让子系统内部地模块能更容易扩散和维护。

  • 简单易用:客户端不需要了解子系统内部地实现,也不需要跟众多子系统内部的模块进行交互,只需要跟外观模式交互就好了。

  • 更好的划分访问的层次:有些方法是对系统外的,有些方法是对系统内部使用的。把需要暴露的功能集中放到外观中,方便客户端使用,还隐藏了内部的细节。

缺点:

  • 过多的或者是不太合理的 Facade 也容易让人迷惑。到底是调用 Facade 好,还是直接调用模块好。

3.3.4 思考外观模式

  1. 外观模式的本质
外观模式的本质是:封装交互,简化调用。
  1. 对设计原则的体现

外观模式很好地体现了“最少知识原则”。

  1. 何时选用外观模式
  • 为一个复杂的子系统提供一个简单接口
  • 让客户程序和抽象类的实现松散耦合
  • 构建多层结构的系统

猜你喜欢

转载自blog.csdn.net/qq_37581282/article/details/82963262