posts - 80,comments - 749,trackbacks - 2
又是很久没有写Blog了,这两天大量的时间贡献给了两件事,一是看Eclipse的源代码(工作需要),二是不断回复以前写的文章的评论。今天请了假, 抽了点时间来讨论讨论重构。下面写的东西您可能不太赞同,可能没有遇到,也可能有其它更好的办法,而我的观点来自于我对Eclipse源代码的理解和感 悟,无论您有什么想法请评论告诉我,不甚感谢。

1。如果让您随手写一个类,多半人会写出一个以名词命名的类,是的,我们的软件中大量的类是以名词来命名的。一个名词表明了一组对象的共同类型,那么这个类型一定包括该组对象的共同属性和共同方法。

2。在几次迭代之后我们发现类的属性和方法都增多起来,这种粗放型的发展不利于软件系统的整体结构。而事实上,有很多类在80%的时间只需用到20%的属性代码,而20%的时间却需要用到80%的方法代码,因此,将访问率不同的属性和方法分开是必然的趋势。于是有了Descriptor模式,它将一个原始类分成属性集中化和方法集中化的两个类,属性类的命名方式采用原类名+Descriptor的方式,方法类可能沿用原名称,或重新取个名字以动词命名。这种重构还有一个好处就是属性类随时可以加载,而方法类可能要到需要用时才懒加载。

3。在经过多次迭代的增加该类的代码之后,我们发现Descriptor类的属性增多起来,过多的属性使代码变得重量化,同时使结构变得不清晰,一个拥有 过多属性的类也会因知识过多而不易维护,此时最好的方法是将这个类分裂为两个,或多个。分裂的方法也有横向和纵向两种。横向的分裂使类变成两个不同的类, 它们往往叫两个不同的名字且都为名词,同时也可能互相持有对方(Change Unidirectional Association to Bidirectional

4。纵向的分裂方法(Extract Class)往 往会将比较公共的部分分离取名为原类名+Model,另一个类名称不变,Model类是后者的父类。加过Model以后的类轻磅了很多,更便于管理,责任 也更轻。至于哪些属性应该放在父类界限不是很严格,通俗的办法是和原类名紧密相关的(特有的)属性应该留在原处,不是紧密相关的(特有的)放在Model 类,比如ID、Label什么的就应该放在Model类。

5。上面的方法反复用几次,属性集中的类就会变得越来越多。这时软件结构虽然看的很清晰,可是使用起来大为不便,因为很多相关的属性可能会在同一个场合下使用,而它们的实体却可能分布在不同的对象中。解决这个问题的方法只有增加接口的数量(Extract Interface),多使用一些小接口,每个小接口表达了一个方面的含义,使用者在使用这些小接口时无法触及它的实体对象(Prototype), 这种方法的优劣性在于使用者对于架构的理解,如果用的好这种方法会显现出很多面向方面的特性,如果用的不好则是画蛇添足,导致了接口的泛滥(还记得Dll Hell吗,Java是不是正在形成一个Interface Hell?Eclipse是不是正在形成一个Plugin Hell?)。

6。在大量应用了方面接口之后,我们又面临着这样的问题,很多使用这些Model的人其实只是关心其中的一部分固定属性,和几个为数不多的固定方法,有时连重量级方法都不关心,只关心用以交互的查找型get方法,对于这些使用者,我们只需提供一个门面(Facade)即 可。一个Facade包括在一个Model类和Descriptor类的群体中提供一组可以找到任何一个属性的线索,其中的每一个线索都开始于 Facade,结束于存放对应属性的属性类,且遵循“最常用到的属性,其线索最短”的原则。因而Facade绝不仅仅是一个类,而是一个完整的体系结构。

7。接下来还有一个持久化的问题。如果一个类型存在对一个复杂类型的引用,这个复杂类型很可能被深深的隐藏在Facade之后,而且很可能是个不可持久化 的类型,那么如果前者要持久化,它只能保存一个该复杂类型的关键码,并在持久化唤醒时很容易通过某个服务取到该关键码所对应的对象。这个复杂过程没理由要 求Facade以外的类来完成,因此有了Reference模式(Change Value To Reference),即为那个复杂类型定义一个类,类名为原类名+Reference,其责任是保存原类型的关键码和查找原类型的真实对象。Reference类通常定义为可持久化。该模式的另一个好处是不必要常常访问Facade,因为一个Facade也有可能极其复杂。

8。在分析完属性集中化的类和类群之间的关系之后,我们来看看方法集中化的类。这个类也有可能面临方法激增的问题,此时想把一个类拆分成多个可没有拆分属 性集中化的类那么容易了,因为它们往往相互调用,耦合度极高,但又不能不拆(还记得Kent Beck曾经说过的吗,“如果一个类的责任超过三个,我们就必须把它拆开以保证每个类的责任都不超过三个”)!幸好我们有Delegate模式可 供选择,一个方法类可以由它的访问子和工作子两个角色来完成,就像销售部门和生产部门一样。访问子还以原类名来命名,但是它不做实际的工作,只作实际工作 的准备工作(比如收集信息)和后续工作(比如转换结果),生产性的工作交给工作子完成,后者通常以原类名+Delegate命名。这样做的另一个好处就是 Delegate类可能很重磅,并面临大量的资源分配,因而可以懒加载。

9。现在我们有了一个不错的体系结构,它包括一些轻量级的类、类的关系和设计模式,但是,不要以为这些东西是开始时就设计好了的,即使神仙也做不到那样,令人惊奇的是,它们都是从刚刚您写的一个名词开始的。^_^

经常重构的泡泡

posted on 2005-04-13 10:54 Brian Sun 阅读(2617) 评论(4)  编辑  收藏 所属分类: 软件

FeedBack:
# re: 高级重构方法的连用
2005-04-13 17:19 | tacyuko
通宵3天的翻译结束了,好开心啊
看到你的长文,好郁闷啊  回复  更多评论
  
# re: 高级重构方法的连用
2005-04-13 21:00 | idior
属性扩展的现象在个人应用中还真不是那么明显。在其他书上似乎也很少提到,泡泡个人感觉如何?(除了在eclpise)
还有责任分配的那个模式gof叫做bridge。用XXXdelegate来取类名感觉会影响该类的重用性。(被限于XXX使用)  回复  更多评论
  
# re: 高级重构方法的连用
2005-04-14 17:26 | Brian Sun
呵呵,谢谢指点,我也觉得delegate的使用确实有些局限,但是在Eclipse当中,情况是这样的,在Eclipse中一个Action往往在窗体初始化时就需要关于它的很多属性和特征,但是它的执行实体却只有在用户点击触发这个Action时才会被懒加载,所以一个Action会把自己的执行体包装在一个ActionDelegate当中。还有什么问题,请指正。。。。谢谢!

泡泡
  回复  更多评论
  
# re: 高级重构方法的连用
2007-06-21 13:38 | FrankShaka
怎么以前没发现你有这篇文章?

在网上找了半天,好像只有你这里讲到了Descriptor模式和Reference模式。现在Reference模式我用的很爽啊,哈哈。。。。。  回复  更多评论
  

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


网站导航: