又是很久没有写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) 编辑 收藏 所属分类:
软件