Android高效进阶:从数据到AI【3.9】

10.3.2 适配器模式

适配器模式是把一个类的接口转换成所期待的另一种接口,从而使原本因接口不匹配而无法一起工作的两个类能够一起工作。

好处: 增加了类的透明性和复用性。将具体的实现封装在适配者类中,对于使用者来说是透明的,而且增加了适配者的复用性。

适配器模式对应的类图设计如图 10-12 所示。

Android 中适配器模式的应用有: ListView 中的 Adapter(如 CursorAdapter 将 Cursor 类型接口转换成目标角色 ListAdapter 的目标接口,让 ListView 调用)和 GridView 等。

10.3.3 命令模式

命令模式是把一个请求或者操作封装在命令对象中。命令模式允许系统使用不同的请求参数化客户端,为请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

好处: 把发出的责任和执行命令的责任分割开。命令模式对应的类图设计如图 10-13 所示。

Android 中命令模式的应用有: Runable( Handler 中的 post 方法)。

10.3.4 建造者模式

建造者模式是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

使用场景: 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时,以及当构建过程必须允许被构建的对象有不同的表示时。

建造者模式对应的类图设计如图 10-14 所示。

Android 中建造者模式的应用有: AlertDialog 可以有不同的样式和呈现方式,这样就可以通过 Builder 有效实现构建和表示的分离。

10.3.5 享元模式

享元模式是运用共享技术有效地支持大量细粒度的对象。它的本质是分离与共享。享元模式对应的类图设计如图 10-15 所示。

下面是具体的代码实现:
1. public class TestFlyweight
2. {
3. private static List<Order> orders = new ArrayList<Order>();
4. private static FlowerFactory mFlowerFactory;
5.
6. private static void takeOrders(String flavor)
7. {
8. orders.add(mFlowerFactory.getOrder(flavor));
9. }
10.
11. public static void main(String[] args)
12. {
13. mFlowerFactory = FlowerFactory.getInstance();
14.
15. takeOrders("red");
16. takeOrders("blue");
17. takeOrders("green");
18. takeOrders("green");
19. takeOrders("yellow");
20. takeOrders("blue");
21. takeOrders("yellow");
22. takeOrders("blue");
23. takeOrders("red");
24. takeOrders("green");
25. takeOrders("blue");
26. takeOrders("red");
27. takeOrders("green");
28. takeOrders("yellow");
29. takeOrders("yellow");
30.
31. for (Order order : orders)
32. {
33. order.sell();
34. }
35. System.out.println("
 total buy " + orders.size() + " flowers! ");
36. System.out.println("total generate " + mFlowerFactory.
getTotalFlowerMade() + " FlowerOrder java object! ");
37. }
38.
39. public abstract class Order
40. {
41. public abstract void sell();
42. }
43.
44. public class FlowerOrder extends Order
45. {
46. public String mFlower;
47.
48. public FlowerOrder(String flower)
49. {
50. this.mFlower = flower;
51. }
52.
53. @Override
54. public void sell()
55. {
56. System.out.println("sold one " + mFlower + " color flower");
57. }
58. }
59.
60. public static class FlowerFactory
61. {
62. private Map<String, Order> mFlowerPool = new HashMap<String,
Order>();
63.
64. private FlowerFactory()
65. {
66. };
67.
68. private static class FlowerFactoryHolder
69. {
70. private static FlowerFactory instance = new FlowerFactory();
71. }
72.
73. public static FlowerFactory getInstance()
74. {
75. return FlowerFactoryHolder.instance;
76. }
77.
78. public Order getOrder(String flower)
79. {
80. Order order = null;
81. if (mFlowerPool.containsKey(flower))
82. {
83. order = mFlowerPool.get(flower);
84. }
85. else
86. {
87. order = new TestFlyweight().new FlowerOrder(flower);
88. mFlowerPool.put(flower, order);
89. }
90. return order;
91. }
92.
93. public int getTotalFlowerMade()
94. {
95. return mFlowerPool.size();
96. }
97. }
98. }

Android 中享元模式的应用有: String、 Message 和 ServiceManager 等。

10.3.6 备忘录模式

备忘录对象是一个用来存储另一个对象内部状态快照的对象。备忘录模式的用意是,在不破坏封装的前提下,捕捉( Capture)一个对象的状态并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。

好处: 有时一些发起人对象的内部信息必须保存在发起人对象以外的地方,但是必须由发起人对象自己读取,这时使用备忘录模式可以把复杂的发起人内部信息相对于其他对象隔离开,从而可以恰当地保持封装的边界。

备忘录模式对应的类图设计如图 10-16 所示。

在图 10-16 中, Invoker 表示用户, Originator 表示网站, Memento 表示 Cookie, CareTaker表示浏览器。

在 J2EE 框架中有一个典型的备忘录模式的应用。 Java 引擎提供 Session 对象,可以弥补HTTP 协议的无状态缺点,存储用户的状态。当一个新网络用户调用一个 JSP 网页或者 Servlet时, Servlet 引擎会创建一个对应于这个用户的 Session 对象,具体的技术细节可能是向客户端浏览器发送一个含有 Session ID 的 Cookie,或者使用 URL 改写技术将 Session ID 包含在 URL 中等。在一段有效时间内,同一个浏览器可以反复访问服务器,而 Servlet 引擎可以使用这个 Session ID 来判断来访者应当与哪一个 Session 对象对应。 Session 和 Cookie 使用的都是备忘录模式。

网站可以通过浏览器读取 Cookie 中的信息,而浏览器仅保存传递过来的 Cookie。对应的实现如下:

1. public class TestMemento
2. {
3.
4. public static void main(String args[])
5. {
6. Originator o = new TestMemento().new Originator();
7. Caretaker c = new TestMemento().new Caretaker();
8.
9. o.setState("On");
10. c.saveMemento(o.createMemento());
11. o.setState("Off");
12. o.restoreMemento(c.retrieveMemento());
13.
14. System.out.println(o.getState());
15. }
16.
17. public class Memento
18. {
19. private String mState;
20.
21. public Memento(String state)
22. {
23. this.mState = state;
24. }
25.
26. public String getState()
27. {
28. return mState;
29. }
30.
31. public void setState(String state)
32. {
33. this.mState = state;
34. }
35. }
36.
37. public class Originator {
38.
39. private String mState;
40.
41. public Memento createMemento(){
42. return new Memento(mState);
43. }
44.
45. public void restoreMemento(Memento memento){
46. this.mState = memento.getState();
47. }
48.
49. public String getState() {
50. return mState;
51. }
52.
53. public void setState(String state) {
54. this.mState = state;
55. System.out.println("current state: " + this.mState);
56. }
57. }
58.
59. public class Caretaker {
60.
61. private Memento mMemento;
62.
63. public Memento retrieveMemento(){
64. return this.mMemento;
65. }
66.
67. public void saveMemento(Memento memento){
68. this.mMemento = memento;
69. }
70. }
71. }

Android 中备忘录模式的应用有: Canvas 和 Activity。

10.3.7 观察者模式

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。它又 叫 发 布 – 订 阅 ( Publish-Subscribe ) 模 式 、 模 型 – 视 图 ( Model-View ) 模 式 、 源 – 监 听 器( Source-Listener)模式或从属者( Dependents)模式。

观察者模式对应的类图设计如图 10-17 所示。

Android 中观察者模式的应用有: BaseAdapter。

10.3.8 原型模式

原型模式是通过复制一个已存在的实例来返回新实例的,而不是新建实例。被复制的实例就是所谓的原型,这个原型是可定制的。

原型模式是一种创建型的设计模式,主要用来创建复杂对象和构建耗时的实例。通过克隆已有的对象来创建新对象,从而节省时间和内存。通过克隆一个已经存在的实例,可以使我们的程序运行得更高效。

原型模式对应的类图设计如图 10-18 所示。

Android 中原型模式的应用有: Bitmap 中的 copy 方法、 Java 中的 clone 方法和 Animator 等。

10.3.9 代理模式

代理模式是给某一个对象提供一个代理对象,并由代理对象控制对原对象的应用。通俗地讲,代理模式就是我们生活中常见的中介。代理模式类型图如图 10-19 所示。

Android 中的 Binder 机制就是对代理模式的经典实现。

1. Binder 概述

在计算机中,为使两个不同进程中的对象能够互相访问,需要使用跨进程通信技术。传统的跨进程通信机制有: Socket、内存共享、消息队列等。

由于传统的跨进程通信机制开销大、安全性低, Android 系统自己设计了一个新的通信机制 Binder。

Binder 基于 Client-Server 通信模式,为发送方添加 UID/PID 身份,在确保传输性能的同时又增强了安全性。

2. Binder 的四大核心模块

如图 10-20 所示, Binder 的四大核心模块有 Client、 Server、 Service Manager 和 Binder Driver。

在图 10-20 中, Client 相当于客户端, Server 相当于服务器, Server Manager 相当于 DNS服务器, Binder Driver 相当于路由器。其中, Binder Driver 在内核空间中实现,其他三者在用户空间中实现。

( 1) Binder Driver

Binder Driver 主要负责 Binder 通信的建立,以及其在进程间的传递和 Binder 引用计数管理/数据包的传输等。 Client 和 Server 之间的跨进程通信统一通过 Binder Driver 处理和转发。

( 2) Client

Client 只需要知道自己要使用的 Binder 的名字及其在 Server Manager 中的引用即可获取该Binder 的引用,得到引用后就可以像调用普通方法一样调用 Binder 实体的方法。

( 3) Server

Server 在生成一个 Binder 实体时会为其绑定一个名字并传递给 Binder Driver, Binder Driver 会在内核空间中创建相应的 Binder 实体节点和节点引用,并将引用传递给 Server Manager。

Server Manager 会将该 Binder 的名字和其引用插入一张数据表中,这样 Client 就能够获取该 Binder 实体的引用并调用上面的方法。

( 4) Server Manager

Server Manager 相当于 DNS 服务器,负责映射 Binder 的名字及其引用。其本质同样是一个标准的 Server。

Server Manager 和 Client 端 的 ServiceManagerNative 、 ServiceManagerProxy 都 实 现 了IServiceManager 接口,该接口定义了 Server Manager 对外公布的方法,这样 Client 就能通过这些方法获得 Binder 引用。

从 Binder 的四大核心模块可以看出,其与 Java RMI 有许多相似之处,但其实 Android 的Binder 机制是一个庞大的体系模块,实现要复杂得多。

当我们想使用 Binder 进行进程间通信时, Android 已将 Binder Driver 和 Server Manager 封装得很完美了,我们只需实现自己的 Client 和 Server 即可。

Android 提供了 AIDL 这种简便的方式来快速实现 Binder。3. AIDL

AIDL 是 Android 接口定义语言,使用它能够快速地实现 Binder 来进行进程间通信。

使用 AIDL 来进行进程间通信的流程分为服务端和客户端两个方面,下面分别进行介绍。

( 1)服务端:服务端需要创建 Service 来监听客户端的请求,创建一个 aidl 文件声明公开的方法。在这里, aidl 文件相当于“远程接口”, Service 相当于“服务对象”。

( 2)客户端:客户端需要绑定服务端的 Service,并将服务端返回的 Binder 引用转换成

AIDL 接口所属的类型。AIDL 的具体实现如下。

( 1)创建 AIDL 接口

1. // IMyManager.aidl
2. package com.alv.aidl;
3.
4. interface IMyManager {
5. String sayHello();
6. }

注意: AIDL 的包结构在服务端和客户端要保持一致,因为客户端需要反序列化服务端中与 AIDL 接口相关的所有类。

在创建项目后,系统会根据 IMyManager.aidl 生成一个 Binder 类。 Binder 类内部包含一个Stub 类和 Proxy 类。

值得注意的是,在这里生成的 Stub 类相当于“远程接口实现类的抽象类”,在 Service 中将实现 Stub 中的抽象方法,而 Proxy 类是提供给客户对象的代理类。

( 2)实现远程服务端 Service

先创建一个 Service,然后创建一个 Binder 对象实现 Stub 中的内部方法并在 onBind 方法中返回它:

1. private Binder mBinder = new IMyManager.Stub() {
2. @override
3. public String sayHello() throws RemoteException {
4. return "server says hello";
5. }

( 3)实现客户端

客户端只要绑定连接 Service,将服务端返回的 Binder 对象转换为 AIDL 接口,就能够调用服务端的方法。关键代码如下:

1. IMyManager myManager = IManager.Stub.asInterface(service);

2. String hello = MyManager.sayHello();

可以看到,连接成功后, asInterface 会返回 Proxy 代理对象,之后再将 Proxy 代理对象转换成 IHelloManager 接口。

这样我们在客户端就能够远程调用不同进程里的方法了。

10.3.10 状态模式

状态模式是把所研究对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的子类。状态模式的意图是让一个对象在其内部状态改变的时候行为也随之改变。

好处: 每个状态会被封装到独立的类中,这些类可以独立变化,而主对象中没有烦琐的swich-case 语句,并且非常容易添加新状态,只需要从 State 派生一个新类即可。

状态模式对应的类图设计如图 10-21 所示。

下面是具体的代码实现:
1. public class TestState
2. {
3.
4. public static void main(String args[])
5. {
6. Context context = new TestState().new Context();
7.
8. StartState startState = new TestState().new StartState();
9. startState.doAction(context);
10.
11. System.out.println(context.getState().toString());
12.
13. StopState stopState = new TestState().new StopState();
14. stopState.doAction(context);
15.
16. System.out.println(context.getState().toString());
17. }
18.
19. public class Context
20. {
21. private State mState;
22.
23. public Context()
24. {
25. mState = null;
26. }
27.
28. public void setState(State state)
29. {
30. this.mState = state;
31. }
32.
33. public State getState()
34. {
35. return mState;
36. }
37. }
38.
39. public interface State
40. {
41. public void doAction(Context context);
42. }
43.
44. public class StartState implements State
45. {
46.
47. @Override
48. public void doAction(Context context)
49. {
50. System.out.println("Player is in start state");
51. context.setState(this);
52. }
53.
54. public String toString()
55. {
56. return "Start State";
57. }
58. }
59.
60. public class StopState implements State
61. {
62.
63. @Override
64. public void doAction(Context context)
65. {
66. System.out.println("Player is in stop state");
67. context.setState(this);
68. }
69.
70. public String toString()
71. {
72. return "Stop State";
73. }
74.
75. }
76. }

Android 中状态模式的应用有: StateMachine。

设计模式是一套被反复使用、多数人知晓、经过分类编目的代码设计经验的总结。使用设计模式是为了更好地重用代码、让代码更容易被他人理解和保证代码可靠性。

毫无疑问,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。在项目中合理地运用设计模式可以完美地解决很多问题,每一个设计模式都描述了一个我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它们能被广泛应用的原因。

本章只是以部分设计模式为引来说明设计模式在 Android 源码中的应用,要想真正理解和用好设计模式,还需要开发者不断实践,在实践中不断地领悟设计模式的精髓。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容