设计模式二十之外观模式

通过理论,代码示例,Android源码来学习外观模式

介绍

外观模式 (Facade) 在开发过程中的运用评率非常高,尤其是在现阶段,各种第三方 SDK “充斥” 在我们周边,而这些 SDK 大多会使用外观模式。通过一个外观类使得整个系统的接口只有一个统一的高层接口,这样能够降低用户的使用成本,也对用户屏蔽了很多实现细节。当然,在我们的开发过程中,外观模式也是我们封装 API 的常用手段,例如,网路模块、图片模块,可能你已经在开发中运用无数次外观模式,只是没有在理论中认识它,下面我们就来学习它。

定义

要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。

使用场景

  1. 为一个复杂子系统提供一个简单接口。子系统往往因为不断演化而变得越来越复杂,甚至可能被替换。大多数模式使用时都会产生更多、更小的类,这使子系统更具可重用性的同时也更容易对子系统进行定制、修改,这种易变性使得隐藏子系统的具体实现变得尤为重要。Facade 可以提供一个简单统一的接口,对外隐藏子系统的具体实现、隔离变化。
  2. 当你需要构建一个层次结构的子系统时,使用 Facade 模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过 Facade 接口进行通信,从而简化了它们之间的依赖关系。

UML 类图

  • Facade: 系统对外的统一接口,系统内部系统地工作。

代码示例

需求背景:手机的外观模式(电话功能,短信功能,GPS, 拍照)

手机抽象类:

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
public abstract class AbstractMobile {
/**
* 定义一个打电话功能
*/
protected Phone phone = null;

/**
* 定义一个拍照功能
*/
protected ICamera camera = null;

public AbstractMobile(Phone phone, ICamera camera) {
this.phone = phone;
this.camera = camera;
}

public abstract void dail();

public abstract void videoChat();

public abstract void hangup();

public abstract void takePicture();

public abstract void stop();
}

手机实现类:

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
37
public class MobilePhone extends AbstractMobile {


public MobilePhone(Phone phone, ICamera camera) {
super(phone, camera);
}

public void dail() {
phone.dail();

}

;

@Override
public void videoChat() {
System.out.println("视频聊天呼叫中...");
camera.openCamera();
}

@Override
public void hangup() {
phone.hangup();
}

@Override
public void takePicture() {
camera.takePicture();
}

@Override
public void stop() {
camera.stop();
phone.hangup();
}

}

功能接口类

1
2
3
4
5
6
7
8
9
10
11
public interface Phone {
/**
* 打电话
*/
public void dail();

/**
* 挂断
*/
public void hangup();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface ICamera {

/**
* 打开相机预览
*/
public void openCamera();

/**
* 拍照
*/
public void takePicture();

/**
* 关闭相机
*/
public void stop();
}

功能实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class CameraImpl implements ICamera {
@Override
public void openCamera() {
System.out.println("打开相机");
}

@Override
public void takePicture() {
System.out.println("拍照");
}

@Override
public void stop() {
System.out.println("关闭相机");
}
}
1
2
3
4
5
6
7
8
9
10
11
public class PhoneImpl implements Phone {
@Override
public void dail() {
System.out.println("打电话");
}

@Override
public void hangup() {
System.out.println("挂断电话");
}
}

test:

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void testFacade(){

Phone phone = new PhoneImpl();
ICamera camera = new CameraImpl();
MobilePhone mobilePhone = new MobilePhone(phone,camera);

mobilePhone.dail();
mobilePhone.videoChat();
mobilePhone.takePicture();
mobilePhone.stop();
}

output:

1
2
3
4
5
6
打电话
视频聊天呼叫中...
打开相机
拍照
关闭相机
挂断电话

从上述代码可以看到,外观模式就是统一接口封装。将子系统的逻辑、交互隐藏起来,为用户提供一个高层次的接口,是的系统更加易用,同时也对外隐藏了具体的实现,这样即使具体的子系统发生了变化,用户也不会知道,因为用户使用的是 Facade 高层次接口,内部的变化对于用户来说并不可见。这样一来就将变化隔离开来,使得系统更为灵活。

总结

外观模式是一个使用频率较高的设计模式,它的精髓就在于 ”封装“ 二字。通过一个高层次结构为用户提供统一的 API 入口,使得用户通过一个类型就基本能够操作整个系统,这样减少了用户的成本,也能够提升系统的灵活性。

优点:

  1. 对客户程序隐藏子系统细节,因而减少了客户对于子系统的耦合,能够拥抱变化。
  2. 外观类对子系统的接口封装,使得系统更易于使用。

缺点:

  1. 外观类接口膨胀。由于子系统的接口都有外观类统一对外暴露,使得外观类的 API 接口较多,在一定程度上增加了用户使用成本。
  2. 外观类没有遵循开闭原则,当业务出现更换时,可能需要直接修改外观类。
坚持原创技术分享,您的支持将鼓励我继续创作!