一段与网友对话引出的设计问题
作者: coffeewoo(http://coffeewoo.itpub.net)发表于: 2006.06.23 00:00
分类: 系统架构
出处: http://coffeewoo.itpub.net/post/9169/125431
---------------------------------------------------------------
与网友的一番讨论,引出了一个设计方面的问题。 当某个信息是可能扩展的,而系统中很多模块都要适应这个变化,最基本的要求是这个扩展不能影响到以前的程序,最好的结果是这个扩展能够被自动适应,该如何设计?
先看看对话吧,这是问题的引出过程。
| L | 今天想请教一个问题~有空吗 --------------------------- |
| coffeewoo | 还行,你说吧 --------------------------- |
| L | 谢谢~我的问题大概是这样一个样子: 构建一个多种业务关系的组织架构模型。 -------------------------------------------------------------------------------- 功能要点描述: 1.组织架构的数据实体类由开发人员创建。如公司类、部门类、岗位类、人员类等 2.各数据实体类之间的关系可由用户定义,比如:上下级关系、平级关系、项目合作关系等 3.用户首先通过系统提供的界面选择相关数据实体类,并定义类之间的关系(包括主关系和其它业务关系)。如:公司类与部门类属上下级关系。 4.用户再依据3定义的类关系完成具体类的实例间的相互关系(如:A公司与A1部门是上下级关系,B公司与B1部门是上下级关系,而A1与B1还有项目合作关系等)后,系统能按用户定义的关系完成多种关系的实现和业务运转。 --------------------------- |
| coffeewoo: | 你的设想呢? --------------------------- |
| L | 我的问题是这样来的~目前的系统都是针对某个组织当前时刻的一个稳定状态构建的管理系统,而不能应对随时可能产生的组织机构变更~如果一旦变更,就需要修改很多地方~我想通过配置的方法来实现组织机构的人为重组 --------------------------- |
| coffeewoo | 首先我肯定你这个想法。的确也可以用很好的架构来实现这种配置。不过在这之前我先提醒一下,组织架构是一个业务系统最基础的架构,这个树形结构的枝一般情况下是极少变动的,叶变动又不影响结构。如果不是特殊原因,我建议是不要让组织结构成为可配置的。原因在于,与这个部分牵连的内容太多,比如授权体系,数据权限体系,业务流程体系,人员体系...也就是说,如果这部分是可变的,那么你整个系统的复杂度将成成指数级上升。你想想,如果只是组织机构可变,但这个变化不能实时的反映到其它模块,这个变化又有何用呢? --------------------------- |
| L | 我之所以提出这样的问题,是确实存在着这样的一个需求: 1:机构组织树本身存在变更的可能性,但自身存在逻辑关系,可以通过逻辑最终确定组织树的某时刻稳定状态; 2:确实复杂度会上升很多,但是我想在基础树上产生的所有资源管理工具是否有通用办法可以实现在基础树变更的同时实现相关资源的自适应呢? --------------------------- |
| coffeewoo | 能先说说你的设想吗?你打算怎么做? --------------------------- |
| L | 我的所面对的组织基础树可能较此要简单些:我打算将组织抽象成实体类,然后把组织间关系抽象成关系类,通过实体连接关系 实现基础树的动态变更,相关资源也会通过关系作出相应改变 --------------------------- |
| coffeewoo | 这个思路是不错的啦 --------------------------- |
| coffeewoo | 但是有一个问题,相关资源如何获得这个关系的变更?某一资源如何得知某关系已经从上下级变成了同级?上下级和同级对这个资源来说,它凭什么去区分? --------------------------- |
| L | 我以为:首先实现基础树~再在基础树上构建~如何~每次的变更都遵照此顺序,这样资源可以通过基础树的关系区分~这样是否可行呢? --------------------------- |
| coffeewoo | 你之前所说的思路是没有错的,把实体和关系抽象出来,用关系来表达两个实体之间的业务关系。我的问题是,在这种情况下,这个关系就失去了特征。举个例子,在不配置的情况下,实体关系是固定的,A部门和B部门一定是上下级关系,所以其它模块一定是知道这个关系的,没得选择。在配置的情况下,如果关系类型是枚举形的,比如上下级,同级,你可以用代码1,2来表示,那么其它模块也能够通过判断是1还是2来获知A部门和B部门的关系。但是如果按你所说,关系本身是可以增加和减少的,那么其它模块就根本无从得知A部门和B部门是什么关系了,因为这个关系是随时可能变化的。 --------------------------- |
| L | 我认为这个关系的随时变化正是配置啊 --------------------------- |
| coffeewoo | 是啊,我举个例子问你,如某项查询结果,如果A,B部门是上下级关系的情况下,这个结果显示的是A能看到A和B的数据,B只能看到B的数据。如果把AB的关系改为了同级关系,要求是A只能看A的,B只能看B的。你在不改动程序的情况下,怎么保证查询模块适应这个变更? --------------------------- |
| L | 首先,基础树丛状态1(父子)到状态2(同级),两个状态在某时刻都是稳定的(存在稳定的关系),此时的查询模块会首先获取状态2的关系 ,根据2的关系得出查询到的数据便是状态2本该获取的数据,这里的状态2对于资源(查询模块)是前置的 --------------------------- |
| coffeewoo | 注意哦,状态1,状态2,你是用特征1,2来区分的。这就是说,你的关系状态是个枚举形的,是事先定义好的。那么这个关系本身是不会变化的,1就是1,2就是2,不可能突然出现一个3,也不可能把1删除或把2的含义从同级关系改变成合作关系,对吗? --------------------------- |
| L | 是啊~怎么办呢~:'( --------------------------- |
| coffeewoo | 呵呵,这是必然的结果啊。一个结构要与外部交互,它必然得提供一个稳定的形式让外界能够获知,这个形式可以有很多种不同的。刚才你所说的就是一种,用枚举代码让外界获知。 --------------------------- |
| coffeewoo | 所以,所有东西都可以变是不可能的,你总得选择一种东西是稳定的,不可变的。你刚才的方案就可以啊,你所有需要做的事情就是考虑到所有可能的关系类型,把它做为一个数据字典,一旦定义好就不能变更(否则不得不改动其它程序)。这个字典你可以存在数据库里,也可以放在配置文件里。总之,让内部和外部都知道,凡是1,就是上下级关系,凡是2,就是平级关系。 --------------------------- |
| L | 这还是一种枚举的关系吧~你刚才说有很多种更高级的办法~我想了解一点~因为我觉得我能想到的这种可能是最自然的简单的方法~会不会有更便捷的新方法可以实现呢 --------------------------- |
| coffeewoo | 有啊,但是比较复杂。你得考虑得非常全面,把所有可能与这个关系变化有有关系的业务都拿出来考虑。比如查询范围,授权,人员,流程....一个都不能漏。然后,抽象一个关系超类,提供一个询问接口,返回给调用这个询问接口的调用者一个值。每一种关系是这个超类的一个泛化。由调用者自己通过询问关系实例来获知这个关系到底是什么关系。 --------------------------- |
| L | 诸如此类的问题~应该属于一个怎样的知识范畴呢? --------------------------- |
| coffeewoo | 我想还是OOD方面的吧 --------------------------- |
| L | 我想系统的学习下这些~但是目前项目工期又紧~所以想走歪门~想速成~ 有没有专门介绍这一个主要问题的东西呢 --------------------------- |
| coffeewoo | 建议你认真去看看设计模式方面的资料。网上很多。最重要的是烂熟每个模式的意图,形式反而不是最重要的 --------------------------- |
| coffeewoo | 关于你这个问题,可以参考一下Observer模式中Observer是如何向Subject询问变更的。 --------------------------- |
| L | 哈哈~那我先看书了~ --------------------------- |
以上就是讨论的全过程。上述的问题可以归结为一个系统中某个定义的变更可能会引发大量的连琐反应,糟糕的是,我们不能预测这个变更,甚至可能不知道变了什么。系统中的其他模块如何适应这个变更呢?
可以举这么个例子,假设有一个按身份入住的宾馆,如果来的是学生,就住学生间,来的是老师,就住教师间。并且我们要为不同身份的人准备不同的家具,用餐标准等。如果这些身份是固定的,那就很好办了,对号入座就OK。问题是,突然来了一个人掏出一个证件,这个证件我们不认识啊!!!让他住哪个房间呢?该准备什么家具呢?
解决这个问题有两个思路。
一种是创建一本字典,将身份和房间及家俱对照起来,服务人员对照执行。当来了一个新身份的人之后,先往字典里加入该身份应住的房间,应使用的家俱。服务人员读取字典,就能得知该准备什么房间和什么家具。
具体到谈话中的案例来说,就是将新增加的关系先预定义出来,然后具体的程序通过读取配置文件以决定该如何执行。至于实现方式,根据具体情况不同,可以采用Factory、Builder或Strategy模式。
这种解决方案的优点是简单,缺点是不论采用哪种模式,我们都得为新的身份写一套对应的子类来完成新的工作。另一个局限性是新增加的关系不能是由客户任意增加的,因为新增加的关系类型需要写新的子类。
另一种解决方案是变换思路,证件本身我们不认识,但是我们可以抽象出识别这些证件的关键特征。比如红色外皮的一定是学生证,蓝色外皮的一定是教师证。服务人员不识别证件类型,而根据证件特征来决定如何安排房间。
具体到谈话中的案例,即我们需要将形成关系的规则抽象出来,比方说,A部门的级别是3,B部门的级别是4,我们认为它是一个上下级关系;A部门是是内部的,C部门是外部的,我们认为它是一个合作关系等。但是其它程序并不需要知道到底是上下级关系还是合作关系,它们只是去判断是否具有A级别=3,B级别=4这样的特征以决定如何来执行后续的操作。
这种解决方案比较复杂,如果组成关系的特征是无法抽象的,则不适用。这个方案的目的是将规则抽象出来,用规则来决定程序的运行方式。这个方案有一些局限性,特别适用于关系类型非常多,但都是通过几个简单的规则组合而成的情况,而且每一个或每一组规则都决定了程序的某种运行方式。这样,关系的增加转变为规则组合方式的变更,而所有的组合方式都有对应的处理程序。实现方法,根据实际情况的不同,可以考虑采用Abstract Factory模式、Visitor模式或Chain of Responsibility模式。
不管采取什么解决方案,核心思想只有一个,就是将可能变化的信息中的最不可能变化的部分抽取出来,作为系统适应这个变更的依据。一个系统中不可能什么都是可配置的,就象要描述一件事物的状态必须要有参考系一样,没有参考系系统就是不可确定的。学过大学物理的朋友应该都有体会,参考系的选择会对解题难度大有影响哦。第一种方案可以说我们采用了经典力学的绝对参考系,第二种方案则可以说采用了相对论中的相对参考系。哪种好?能最简便的解决问题的就是最好的。



