这篇说说分类的问题。面向对象的设计从根本上就是一个选择分类的过程,而在分类的过程中,只要不出逻辑上的问题,任何分类都是正确的。所以说,对一个系统进行面向对象的建模,1000个人能建立出1000个不同的模型,但只要建立的模型中没有逻辑问题,都不能说这个建模是错的。只有对系统需求建模的适合与不适合,易扩展与不易扩展之分。而对于开发人员来说,大多数人的思维习惯都是非黑即白的思维,这大概也是真正的对象技术普及不开的原因。
封装,是将分类中的某一层级当成一个最小的不可分割单元来看待。继承,则是对一个单元再进行细分。而多态,就是对封装的行为在细分的过程中消除差异性。由这样的定义可以看到,封装是基础,继承和多态实际是用来处理封装这个单元中的再分类。这三个面向对象的基本概念,构成了一个递归关系,其递归的最终结果,就是我们对象理论构建的基础:Object。
举个例子:在虚拟物品的电子商务系统中谈论订单流程,要求每个虚拟物品的订单都要有一个订单号。而为了在实际操作中可以对订单分类,要求对虚拟物品的每个分类提供一个前缀以区分,比如充值卡的前缀要加上Card,游戏道具的前缀要加上Item。那么根据这样的要求,就可以对虚拟物品这个封装的概念进行细分:充值卡和游戏道具。实际上就是充值卡和游戏道具这两个类继承自虚拟物品这个类。而生成订单号这个操作,很明显对于充值卡和游戏道具是有差异的,因为他们要加的前缀不同,但对于虚拟物品这个概念来说,都是加上前缀这个操作,因此通过override重写,就消除了他们的差异,进行了统一。
一个真正完美的系统,应该是由无数的不可再分割的单元组合而成,即只有封装,没有了继承和多态;就如从物理上看任何东西都是由原子构成。但实际操作中,这样构成的系统的继承层次会非常的深,不易于理解和把握。因此设计师就必须进行合理的取舍,把握住这种递归的层级,使得分类看起来直观和易理解。这也揭示了为什么设计就是权衡的过程。
真正理解了这点,设计模式其实也就不难了。设计模式只是这些概念的具体场景应用,其基本思路就是发现变化、封装变化;或者从分类的角度说就是给你提供了一个比较好的分类的依据。而重构,实际就是发现原来的分类不合理,将其进行分类的重新整理。因此对于设计模式的应用,如果理解的深刻,其实在拿到问题的开始就能够发现一个比较好的分类方式,而根本不用后面再来对分类进行重新整理。《设计模式精解》一书中提到的从分析阶段引入模式,实际就是这个道理。
posted @ 2010-01-31 18:28 枫 阅读(148) 评论(0)
编辑
上面已经谈过了对象的生死问题,那么对于这个对象,来分析其在这个生命周期中的状态变化。
在一个对象创建之后,它就被赋予了一个状态。按照分类的原则,生命周期中的对象可以分为状态改变和状态不改变两类。如果这个对象的状态不改变,那么可以将其定义为不变对象(immutable),比如一个网站的设定,在网站提供服务的这个周期中,这个包含了设定数据的对象就可以看成是不变对象。这个不变对象有个学术名称,叫做Value Object,其很多相关东西以后再说。
那么剩下的就是状态可改的对象。实际上,任何一个系统,不可能只存在Value Object。如果真的有这样的一个系统,那么这个系统没有任何的意义。因此对于任何一个状态改变的对象,在其生命周期中必然需要至少改变一次状态,这个状态的改变的操作就会在生命周期的时间轴上留下一个长度。
同样,对这个操作也存在这样的一个完全分类:在该操作之前、该操作中、该操作之后。由于计算机的线性处理,操作中的这个分类必然需要执行状态改变的操作,因此剩下的两个状态就很重要了,其甚至可以构成无限扩展的基础。例如,在恢复ASP.NET的Page对象的ViewState之前,必须要先确定是否所有的控件已经被创建。一般地我们会把代码写在一起,在恢复之前去验证。但是如果出现这样一个情况,我们自己定义了一个ValidatePage,其继承于Page,我们希望在恢复ViewState之前还要做一个数值验证的工作,那么如果没有源码的话就不能修改了。但是,如果从生命周期的角度,将恢复ViewState之前的这个分类使用RestoringViewState这个事件委托出去,那么即使没有源码,你也能进行扩展。
由于委托需要实际挂接在某个对象之中,因此在该对象执行此委托的时候,其又构成了一个状态的变化,按照这样的循环原则,可以将这个事件无限的传递,这样就构成了无限的扩展。而从实际生活来看,所谓的“蝴蝶效应”就是这样无限扩展的一个实例:蝴蝶扇动翅膀是一个操作,在这个操作之后的事件通过气流状态变化的不断传递,最终构成了美国德克萨斯的龙卷风。注意:这里有个前提,就是这样委托出去的事件不能最终传递到自身。如果当中存在一个循环,那么就无法执行所谓的操作,那么这个操作之前的分类也就无从谈起。而对于之后的这种分类,如果其后面还有另外的逻辑,那么上一个操作的之后与下一个操作的之前是重叠的,因此也可以看做是一个操作的之前,同样也归于之前的一类;而后面没有其他的逻辑,那么说明这个逻辑结束了,也就是其生命周期也结束了,其事件无论怎么传递都不会回到自身了。这也就是为什么事件是以生命周期为基础的操作。
上面所论述的都是对于一个对象来说的,如果一个系统严格按照这样的分类来划分,那太复杂,而且各种委托关系传递到最后自己都不知道传递到哪里去了。因此,实际的系统中都是通过对关键业务流程逻辑来保留事件进行扩展。仔细分析下ASP.NET的Page的生命周期,就会发现微软对一些关键性的事件都提供了之前和之后的两种操作。这也是微软为什么封装了很多东西,但大家使用起来都不觉得很麻烦的原因。
posted @ 2010-01-31 18:27 枫 阅读(124) 评论(1)
编辑