haohao

Android 设计模式之外观模式

Markdown

打造个人品牌是现代职业规划的趋势

在当下互联网革命的时代,有这样一种说法,如果一个人到三十岁以后还是只有工资收入,那么它的职业规划是失败的。
本文是 Android 设计模式的第三篇,接下来会陆续推出整个常用的设计模式系列。

外观模式

外观模式 (Facade Pattern) ,又名门面模式,归类为对象结构型模式,外部对象与一个子系统通信必须通过一个统一的外观对象进行。

模式结构

  • Client 外部对象
  • Facade 外观对象
  • SubSystem 子系统对象

Markdown


盗图

模式实现

电视遥控器是现实生活中一个比较好的外观模式的运用,遥控器可以控制电源 (Power) 的电源、声音 (Voice) 的调整、频道 (Channel) 的切换等。这个遥控器就是我们这里说的外观或者门面,而电源、声音、频道切换系统就是我们的子系统。

PowerSystem

1
2
3
4
5
6
7
8
9
public class PowerSystem {
public void powerOn() {
System.out.println("power on");
}
public void powerOff() {
System.out.println("power off");
}
}

VoiceSystem

1
2
3
4
5
6
7
8
9
public class VoiceSystem {
public void turnUp() {
System.out.println("voice increasing");
}
public void turnDown() {
System.out.println("voice reducing");
}
}

ChannelSystem

1
2
3
4
5
6
7
8
9
public class ChannelSystem {
public void next() {
System.out.println("next channel");
}
public void prev() {
System.out.println("prev channel");
}
}

Facade

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
public class Facade {
private PowerSystem mPowerSystem = new PowerSystem();
private VoiceSystem mVoiceSystem = new VoiceSystem();
private ChannelSystem mChannelSystem = new ChannelSystem();
public void powerOn() {
mPowerSystem.powerOn();
}
public void powerOff() {
mPowerSystem.powerOff();
}
public void turnUp() {
mVoiceSystem.turnUp();
}
public void turnDown() {
mVoiceSystem.turnDown();
}
public void nextChannel() {
mChannelSystem.next();
}
public void prevChannel() {
mChannelSystem.prev();
}
}

Client

1
2
3
4
5
6
public static void main (String[] args) {
Facade facade = new Facade();
facade.powerOn();
facade.turnUp();
facade.nextChannel();
}

Running

power on

voice increasing

next channel

Android 中的实现

在 Activity 中可以进行很多重要的操作,如 startService() ,startActivity() ,sendBroadcast() ,bindService() 以及获取 System Service 。Activity 就可以简单地看作一个门面,但是这些工作实际上并不是 Activity 来实现的,而是委托 Activity 父类 ContextThemeWrapper 中的 mBase 对象,mBase 对象的实现类是 ContextImpl 。

看一下源码:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
class ContextImpl extends Context {
private final static String TAG = "ApplicationContext";
private final static boolean DEBUG = false;
private final static boolean DEBUG_ICONS = false;
private static final Object sSync = new Object();
private static AlarmManager sAlarmManager;
private static PowerManager sPowerManager;
private static ConnectivityManager sConnectivityManager;
private AudioManager mAudioManager;
LoadedApk mPackageInfo;
private Resources mResources;
private PackageManager mPackageManager;
private NotificationManager mNotificationManager = null;
private ActivityManager mActivityManager = null;
...
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
Process.myUserHandle());
}
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
@Override
public String getSystemServiceName(Class<?> serviceClass) {
return SystemServiceRegistry.getSystemServiceName(serviceClass);
}
@Override
public void sendBroadcast(Intent intent) {
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, false, false);
} catch (RemoteException e) {
}
}
@Override
public void startActivity(Intent intent) {
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ " Is this really what you want?");
}
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null, null, intent, -1);
}
@Override
public ComponentName startService(Intent service) {
try {
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()));
if (cn != null && cn.getPackageName().equals("!")) {
throw new SecurityException(
"Not allowed to start service " + service
+ " without permission " + cn.getClassName());
}
return cn;
} catch (RemoteException e) {
return null;
}
}
@Override
public String getPackageName() {
if (mPackageInfo != null) {
return mPackageInfo.getPackageName();
}
throw new RuntimeException("Not supported in system context");
}
...
}

ContextImpl 内部有很多 Manager 类的对象,也就是也就是子系统对象。 ContextImpl 内部封装了一些系统级别的操作以及提供了一些访问系统的接口,我们在开发过程中可以很方便地利用其访问子系统。

外观模式的优点与缺点

优点

  • 屏蔽了子系统内部细节,使子系统的使用更见便利
  • 减少客户端所持对象的数目,降低了客户端与子系统的耦合度

    缺点

  • 增加新的子系统可能需要修改外观类,违背了“开闭原则”

参考



联系我

Wechat ID

公众号

生活不止于眼前的苟且, 还有诗和远方的田野