[实现]
本节描述实现Wrapper Facade模式包括的活动,这些活动可能会有多次迭代。为了节省篇幅,这里深度讨论了mutexes, condition variables, Sockets 和 threads 的Wrapper Facade,而使用Wrapper Facade的别的模式都只是简单的引用此实现过程,这些模式包括:Acceptor-Connector ,Strategized Locking , Thread-Specific Storage , 和 Monitor Object。
1 在已有的底层API中识别出内聚抽象及API之间的关系。成熟的底层API的函数和数据结构定义了内聚的抽象,能清晰地映射到面向对象的类和方法中。(举了些例子,分析了如C这样的语言缺乏数据抽象,表达不清楚哪些API相互关联,因此需要此步骤……)
(上面的日志实现例子中,mutex_lock() 和 mutex_unlock()都关联到 mutex,而socket(), bind(), listen()和 accept()则扮演网络相关角色)
[page 56]
临时代码或piecemeal growth [FoYo99]的代码,可能会体现出很少(或根本没有)内聚抽象,如果有可能,这样的代码应该在实现Wrapper Facade之前重构[Opd92][FBBOR99]。
2 将内聚的一组function聚集到Wrapper Facade类和方法中。(此活动屏蔽了底层细节……)。可分成5个子活动:
1 创建内聚类。作为开始,我们为每组表示特定抽象的相关联的非面向对象API定义一个或多个Wrapper Facade,创建内聚类通常使用如下标准:
- 将高内聚的函数和数据很并到独立的类中,尽量减小类之间的耦合性。(Examples of cohesive functions are those that manipulate common data structures, such as a Socket, a file, or a signal set [Ste98].)
- 区分出底层函数和数据间公共和差异方面。(分别描述公共和差异方面应该涉及些什么。……)
总之,如果原始API包括了大量相关连的函数,那就需要创建多个Wrapper Facade类去分别关注不同方面。
2 将多个独立的函数合并为一个方法。(能保证一组底层函数能按合适的顺序运行。……)
3 如有可能,实现自动创建和销毁操作。(大概说,很多程序中都需要创建数据,而用完后要释放数据,这种过程通常很容易出错。应该用某种自动的机制来解决此问题。……)
4 确定非直接访问的层次。(因为Wrapper Facade需要调用底层API函数,采用inline的方式就没有性能损失,而采用多层调用方式则可以动态绑定方法,也更灵活……)
[page 57]
5 决定在哪里封装特定平台的差异性。Wrapper Facade通常减小了应用程序代码中的平台差异性。虽然Wrapper Facade的方法实现会根据操作系统平台不同而不同,但是他们应该提供平台独立的统一接口。而特定平台的差异性可以通过条件编译或分离目录的方式封装起来:
- 条件编译(Conditional compilation)(采用如#ifdef这样的条件编译指令……)
- 分离目录(Separate directories)(将不同的平台的实现分放在不同目录,编译时根据配置读取不同的实现……)
(在这段中,分析了根据Wrapper Facade接口和实现的改变频率来选则采用哪种平台差异性的封装方式,而且无论采用哪种策略,都应该是由Wrapper Facade的开发者来维护Wrapper Facade,而不应该加重应用程序的开发者的负担。……)
(在例子中引入了Wrapper Facade用以简化日志服务器的实现。……)
(在[page 58-65]中引入了很多日志服务器的实现代码……)
[page 65]
(blah...blah...)
3 考虑允许受控的应用程序访问实现细节。采用Wrapper Facade的好处在于写程序的正确率高(不容易写错误的程序),程序可移植性强,如:socket使用指针来表示,还是用整数来表示,这种特定平台的、易错的实现细节都被Wrapper Facade屏蔽掉了。然而,设计者通常没有预料到,特别抽象、非常强的类型安全性实际上会妨碍程序员有效地使用Wrapper Facade,(?? This experience can be frustrating and may discourage programmers from leveraging other benefits of wrapper facades.)程序员会放弃使用Wrapper Facade带来的其他好处。
要解决太抽象的问题,可以采用如AOP[KLM+97]一样的‘安全窗口(escape hatch)’机制或开放实现技术,这种设计可允许应用程序用受控的方式访问实现细节。
(举了个SOCK_Stream例子,为了支持附加的原始的Socket Wrapper Facade没有提供的功能,引入了访问SOCKET句柄的方法,外部可以通过访问socket句柄就能实现附加功能。)
因为安全窗口机制减弱了Wrapper Facade模式的可移植性,增加了潜在错误的可能性,抵消了Wrapper Facade所带来的好处,当然应该少用这种机制。如果应用程序在某小范围内重复使用特定的安全窗口,则也许表示应该在Wrapper Facade的公共接口中添加一个方法。Extension Interface模式描述了在不影响已有的客户端的前提下添加这种新方法的技术。
4 开发错误处理机制。C语言方式的底层API通常采用返回值,或如errno这样的整数编码,将他们的错误返回给调用者,如果调用者不检查这种返回状态值,则这种技术是很容易出错的。
[page 66]
异常(exception)处理机制是处理错误的更优雅的方式,很多程序语言,如:C++,java等,都将异常作为基础的错误报告机制,另外一些操作系统,如Win32[Sol98]也采用了异常处理机制。将异常处理作为基础的错误报告机制有一下优点:
- 可扩展,(……)
- 与正常处理流程解除耦和,(……)
- 类型安全,(……)
(举了一个异常的例子……)
不幸的是wrapper facade类中的异常也有一些缺点:
[page 67]
- 并不是被所有的程序语言支持。(……)
- 各种语言中的异常实现不同,很难将不同语言实现的能抛出异常的组件集成起来。(……)
- 异常处理导致了多条程序路径,使资源管理变得更复杂。(……)
- 拙劣的异常实现将导致额外的空间或时间上的浪费,(……)
(wrapper facade的缺点在封装核心层的驱动和底层操作系统API时尤其突出,……)
5 定义相关的辅助类(可选)。将底层API封装到wrapper facade类之后,有可能创建其他的辅助类,进一步简化应用程序的开发,这些辅助类通常只有在将底层功能和他们相关的数据都封装到wrapper facade类中过后,才会显现出好处。
(用Thread_Mutex举了个例子……)
[page 68]
(blah...blah...继续例子)
[例子解决方案]
(将[例子]一节的代码采用wrapper facade进行封装过后,最终的代码示例)
[page 69-72]
(都是例子代码)
[page 73]
[已知应用]
MFC(Microsoft Foundation Classes)(……)
ACE(……)
Rogue Wave(……)
ObjectSpace(……)
JVM & Java类库(……)
Simens REFORM(……)
Books consisting of edited collections of papers.(……)
[page 74]
……