Roger Tu

A simple boy living a simple life in every simple day...

   ::  :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  7 随笔 :: 0 文章 :: 19 评论 :: 0 Trackbacks

1         Structural Patterns

描述如何通过类的继承和对象间的组合来解决应用开发中常见问题。

1.1    The Adapter Patten

把一个已有类的某个接口变换成客户端所期待的另一个接口,从而使原本因接口不匹配而无法在一起工作的两个类能在一起工作。

有两种实现方式:

1.1.1   The Object Adapter

       通过对象的组合实现。

1.1.2   The Class Adapter

 

通过类的继承来实现。

1.2    The Composite Patten

用于描述实际应用中经常遇到的对象间的树型层次关系。基本类图:

 

叶子(Leaf)和Branch(树枝)有共同的接口TreeNode,这样,客户端能以一种统一的方式访问树上每个节点。

       Leaf下不能再有其他子节点,所以不支持get/add/remove儿子节点操作。而Branch应该有get/add/remove儿子节点的操作。

       由于LeafBranch支持的操作不完全一致,具体实现可有两种方式:

1.2.1   LeafBranch通过不同接口区分

 

       给定一个TreeNode node,当需要时,通过 if (node instancof IBranch)”或“if (node instanceof ILeaf)”来判断其是Leaf还是Branch,从而获取其所支持的操作。

1.2.2   LeafBranch实现自共同接口

1.2.2.1             共同接口,不同实现


 

       LeafBranch实现自共同的接口TreeNode,该接口定义树结点支持的操作的合集。Leaf对每一个不支持的操作给定一个空实现(即操作完成后,对Leaf节点无任何影响)。这样对于给定的TreeNode node,客户端无需区分是Leaf还是Branch,因为所有操作都可作用于其上。

1.2.2.2             共同接口,相同实现


在共同接口TreeNode中增加“boolean isLeaf()”方法,用于区分节点是Leaf还是Branch

 

Java SwingJTree的树节点就是采取这种设计方式。

1.3    The Decorator Pattern

对于需要通过基本功能的排列组合来产生大功能的应用场景,如果采用继承方式,势必

要为每种可能的组合提供一个子类。而Decorator模式却可以优雅的解决该问题。Java I/O类库的设计就大量应用了Decorator模式。

 

       用户可以按需将多个Decorator的排列组合作用于ConcreteComponent之上,从而为基本function()增加新特性。

1.4    The Proxy Pattern

顾名思义,很好理解。通过Proxy,控制客户端直接创建和访问核心RealSubject

 

       实际应用中,会经常用到该模式,因此Java 2已提供java.lang.reflect.Proxy以及java.lang.reflect.InvocationHandler基础类,使得用户很容易为任一个类动态创建代理。

1.5    The Flyweight Pattern

 

       实际上是单实例创建模式的扩展。ShareObjectFactory负责创建和向外提供有限个ShareObject,这些有限个ShareObject被整个系统共享。ShareObjectFactory自身一般设计为单实例。由于XXXShareObject的实例要被整个系统共享,所以一般被设计为轻量级(Flyweight)的不可变类,而对于依据运行情况可变的外部状态,一般作为类的method的参数传入。

1.6    The Façade Pattern

 

       一个复杂的系统,应该对外提供统一,易于使用的Façade(门面)接口/类。客户端通过Façade与系统交互,而不是直接与该系统的内部组件/类通信。这样,易于设计分层的松耦合的各个子系统;且当一个子系统有变化时,至多对其Façade做调整,对系统其它部分没有影响。

       举个例子,Hibernate作为一个ORM实现,本身是一个复杂的系统。但通过其对外提供的Configure/SessionFactory/Session/Query等接口或类,用户很方便使用,而这些接口或类就是HibernateFaçade

1.7    The Bridge Pattern

1.7.1   示例引入

个人认为,Bridge Pattern是结构模式中最难理解的一个。

好的示例胜于千言万语。假设要设计一个跨平台浏览器,而各种格式图片的显示是该浏

览器的一项重要功能。设当前要支持的图片格式有gifbmpjpgpngpcxtiff六种,而浏览器要支持的运行平台有WindowsMacinotoshUnix三种。一种图片格式的内部数据结构表示(即图片二进制文件格式)显然是固定的,与其将在那个平台显示没有任何关系。而对给定的图片,如何加载和显示与平台相关,不同的平台有不同的加载和显示方式。

需求明了后,如何设计?一种直观的设计是定义一个接口Image,然后为每一种图片格式与平台的组合提供一个实现类,共6×318个。如下图所示:

 

如此设计带来的问题是:

1.子类过多;

2.子类可能有大量重复代码。例如,每个GifInXXX类的load()方法中都有对*.gif文件进行解析的代码片断,而这些代码都是相同的。

3.图片格式与图片显示平台在代码级别相互依赖。假设GIF图片格式内部数据结构发生变化,需要同时修改所有GifInXXX类;假设对Windows平台上图片显示效果有扩展,则需要同时修改所有XXXInWindows类。

因此,需要进一步分析,给出新的设计方案。面向对象分析的一种重要方法就是“找出可变点并对之封装(find what varies and encapsulate it)”。对这个系统,有两个可变点:

可变点1:图片加载/显示方式依据图片格式可变;

可变点2:图片加载/显示方式依据浏览器运行平台可变。

且这两个可变点应能够独立发生变化,无依赖。对可变点2抽象为一个接口PlatformPresentHandler,不同的平台给出不同的实现。不同格式的图片类都有一个对PlatformPresentHandler的引用handlder,用于动态指定图片的显示平台,同时封装仅与各自格式相关的加载/显示代码。完整的图片加载/显示功能由格式相关代码和handler相关method组合完成。最后将所有不同格式图片类进一步抽象为AbstractImage,以便浏览器能以一种统一方式处理不同格式图片。如下图所示:

 

这样设计,好处显而易见:

l         子类数目减少,只需6+3=9个;

l         当新增一种图片格式时,只需增加一个XXXImage类;现有某种图片格式发生变化时,只需修改对应的一个XXXImage类;

l         当新增一种支持平台时,只需增加一个XXXHandler类;对现有某种平台图片显示效果有新要求时,只需修改对应的一个XXXHandler类。

事实上,如此设计应用的就是所谓Bridge Pattern。从类图上来看,AbstractImage/PlatformPresentHandler就像是AXXXImage系列和XXXHandler系列之间的桥梁。

1.7.2   提炼

1.基本类图

 

2.适用场合

       一个构件功能实现依赖多个可变点,而这些可变点又不相互依赖,可以独立变化。
posted on 2007-04-21 13:54 RogerTu 阅读(1121) 评论(0)  编辑  收藏 所属分类: Programming Thought

只有注册用户登录后才能发表评论。


网站导航: