由Avalon所联想起的COP编程技术面临的几大难题
后来,看到Apache Avalon框架,受到很多实质性的启发(技术实现),并且也看到了一个贯彻COP、IOC、SOC、AOP设计概念的系统。
当然了,Avalon是一个失败者,至少目前看来是一个失败者。采用Avalon作为框架的系统似乎只有Apache Web Server本身唯一的一个独苗。
然后,我萌发了“重新改写”Avalon的想法,当然是用DotNet来重新构筑,沿用了Avalon的一些优秀思想,但是从技术上和框架设计上重新制作。(我曾经发过一篇征集Mitochondria开发人的帖子,呵呵,就是上面所说的系统)
这个工作还没有完成,仅仅只开始了一个头,还在初级的设计阶段。
整体设计构想已经经过两次较大变动。
我一直在考虑两个问题,虽然有解决方法,但是效率不好,而且解决的不算很完善:
1、静态绑定问题(也称前期联编问题)
从目前我的10年左右软件经验来看,大多数组件框架都有一个问题没有解决的很好:无论是组件还是位于设计模式中的对象,都或多或少的强迫组件/对象本身或者编写者对于环境Context的假定和深入了解。
用Avalon的一个简化的例子来说明:
MyComponent l_comp = (MyComponent)context.lookup("mycompany.projectA.packageB.MyComponent");
int l_id = l_comp.getID(); //获取实例的ID号码
...
//排错设计、Try...Catch等多余代码都去掉了,此处忽略这些问题
MyComponent是被包含于容器Container中的一个组件,而Lookup用于通过命名来查找组件的实例,该实例由容器根据组件的情况来决定返回哪一个实例。虽然解释很粗糙,但是我想以大家的经验应该完全明白我的意思。
现在问题出现了,当我的另外一个程序(也许是另外一个子系统),需要使用MyComponent功能的时候,那么通常来说,我必须引用MyComponent包。虽然容器解决了运行时的隔离问题,但是却没有办法解决静态对象/组件绑定的问题,而引用的纠缠和复杂性往往造成系统某些方面的混乱,比如DotNet中的RemotingService就是一个这方面的反面教材,在发布客户端对象时,往往不得不做很多额外的处理来解决对象引用问题。
当然,我们可以使用getProperty(string a_propertyName)或者InvokeMethod(string a_method, object[] a_args)这样的通用方法来降低绑定程度。
但是,这些方法往往又是效率极其低下的,因为这些通用方法往往不得不通过反射Reflection和Assembly Load技术来解决动态引用和调用。Reflection和Assembly Load都会降低系统反映的速度,尤其是在多客户并发情况下,比如50个并发的Reflection请求,很可能系统反映速度会下降2-3秒甚至更多。
不知道大家在这方面有没有经验和更好的想法来解决静态绑定问题,仅仅考虑设计模式似乎是不够的,开发技术和技巧可能更重要。
2、组件框架抽象的问题
我在设想一个组件框架系统,如果用户定义组件(即非系统组件或者内核组件)将整个系统看作一个黑箱,组件内所有的操作和反馈都通过R-R(Request-Response)请求-响应模式来处理,那么任何一个用户组件都可以被动态链入和剥离,这对系统的灵活性、伸缩性和可配置性是非常有好处的。
而且我认为R-R模式相对那些复杂的设计模式来说要简单的多,举一个分布式系统的大例子:组件->组件容器A->上级容器ServiceB->顶层容器ServerC->网络传输->异地ServerD->ServerD的ServiceE->ServiceE的组件容器F->组件G完成处理,依同样路径返回数据。
每一级别的容器都使用完全相同的处理规则R-R,自己处理不了,就转发给上级容器,最后从上级容器获得反馈。
我觉得这个模式就组件内部本身来说,规则比较简单,而且比较抽象,不具体假定容器的层次数量、级别数量。
但是问题在于,如何能够描述和准确的判定一个组件是否可以完成某种请求,有没有比较抽象的方法(而不是依赖具体系统具体业务的方法)
,我知道Command模式是一个解决方法之一,但是Command过于依赖既定的命令集规则,缺乏动态性,很容易与具体的业务系统绑定在一起。
不知道大家有没有好的解决方法。其实第二个问题如果能够完美解决,那么也就同时解决了第一个问题:静态绑定的问题。
3、命名服务问题
实现一个抽象、动态、结构性的组件系统,一个好的命名服务框架是不能缺少的。但是我遇到一个问题:如果用独立的命名服务来完成对命名的请求,那么可能造成效率很低。
比如
MyComponentA = (MyComponentA)context.lookup("MyCompany.Project.Package.MyComponentA");
假设包含MyComponentA组件的容器叫做ContainerC,如果ContainerC本身就已经包含了MyComponentA实例,那么何必要将命名请求发送到命名服务上去,因为命名服务必然是一个独立的容器和Service,其间请求和响应来回的时间完全是浪费,因为ContainerC本地就可以完全处理掉。
解决方法到是有,先查询容器ContainerC不就可以了?但是做起来却不容易,考虑一下这种情况:如果ContainerC的命名路径与MyComponentA的命名路径不同,那么ContainerC无法直接通过命名字符串判断是否属于本容器,比如ContainerC的命名是MyCompany.Project.PackageContainer.ContainerC,而MyComponentA则是
MyCompany.Project.Package.MyComponentA,两者的上次命名路径根本不同。如何解决这种复杂情况???
也许可以在容器中保留一份该容器所持有的组件实例的命名清单,这个问题是完全可以解决了,但是新的问题又出现了。
如果容器持有命名清单,意味着容器本身可以解析命名路径,但是解析命名路径并不是简单的拆分以“.”分隔的字符串,因为命名路径中可能包含有资源请求,所以如果容器本身去解析命名路径,就表示容器与命名服务被捆绑在一起了。
这个问题就更不好解决了。
不知道大家有什么高见没有。
先写到这里