BOS的设计缘由

来源:互联网 发布:telnet端口号 编辑:程序博客网 时间:2024/05/13 13:59
 
让自己的工作得到复用,是每一个开发者,尤其是身处设计层次的有经验的开发者的共同心愿。本文就从复用性入手展开讨论,引申出BOS的设计缘由。
 
1.1  问题
观察下述代码(C#),试着找出其复用性不佳之处:
 
interface Shape {…}
class Rectangle : Shape{…}
class Circle : Shape {…}
class Device {…}
 
class Program
{
       static void Main(string[] args)
       {
              Rectangle rect = new Rectangle();
              rect.width_ = 100;
              rect.height_ = 100;
             
              Circle circle = new Circle();
              circle.r_ = 50;
 
              Device dev = new Device();
              dev.Draw( rect );
              dev.Draw( circle );
}
}
 
代码1-1
 
 
作为一个开发者,你一定还对上次别人(或者你自己)复用你的某个方法、类或者框架的情景记忆犹新。这绝对是一件充满成就感并让你体验到什么是价值的事情。但是,由于各种原因,我们并不是每天都能体验到类似的成就感。在很多情况下,我们仍然在撰写复用性并不是那么良好的代码。例如,在上面这段几乎再熟悉不过的简单程序中,隐藏着不止一处损害复用性的细节。下面列出了这些细节:
1)  如果Rectangle类的代码有所变动,那么它和Program类需要重编译(即使Rectangle类和Program类不在同一个文件中)。
2)  如果Circle类的代码有所变动,那么它和Program类需要重编译。
3)  如果Device类的代码有所变动,那么它和Program类需要重编译。
4)  如果初始化配置参数有所变动(例如,圆的半径修改为100),那么Program类需要重编译。
 
初学者对重编译并不引以为然。但是有经验的开发者知道,重编译的代价绝不仅仅是一些CPU的计算时间。在有多人参与的实际项目中,你几乎总是能遇到重编译带来的问题:上下文代码的改变、编译选项不匹配、链接冲突、甚至是因粗心(这在多次改动代码时很常见)而错误删除或改动了部分语句,都会带来意料之外的忙乱和感叹:“这段代码在我的机器上原本是好好的!”
 
1.2  组件是解决方案
为了解决这个问题,我们创造了组件这一伟大的发明。虽然没有明确定义,但组件的全部秘密就在于它避免了重编译。如果一段代码原本可以工作,那么将其封装为组件就可以确保不发生粗心或链接错误。这是因为,组件是以编译后的二进制码存在,而不再涉及源代码。虽然组件仍然会遭遇外部问题(外部引用文件的缺失或系统环境设置不匹配),但它真真切切地避免了“内部”的烦恼。
 
回过头来观察代码1-1,则我们可以确信,将其中三个类—Rectangle、Circle、Device—封装为组件将大大提高复用性。如此一来,问题列表中的1至3可以得到有效解决:Rectangle、Circle或是Device的改动将只局限于自身,而不会传播到Program。这将使整个工程呈现出一种有利于分工、调试以及功能升级的优良结构。
 
具体的做法有很多,你可以使用COM、CORBA等等。无论哪种做法,例程1-1都必然经历与下列代码类似的变动:
 
class Program
{
       static void Main(string[] args)
       {
              Rectangle rect = CreateComponent( component identifier for Rectangle);
              …
              Circle circle = CreateComponent( component identifier for Circle);
              …
              Device dev = CreateComponent( component identifier for Device);
              …
}    
}
代码1-2
 
 
其中,CreateComponent起到了将组件从客户代码分离出去的关键性作用,同时它也是组件系统与客户代码联系的枢纽。CreateComponent的内部实现视你采用何种组件系统而定,但可以肯定的是,它会读取某个动态链接库来获得组件实例—只有通过动态链接库,组件才能成为可复用的二进制代码块。
 
与此相伴的另一个变化是,原有类在成为组件的过程中演变成了两个不同但具备某种联系的类。以Rectangle为例,客户代码中的Rectangle与动态连接库的中Rectangle必须是两个类,这才有可能实现客户代码和组件独立编译。同时,动态链接库中的Rectangle还必须能够转型为客户代码中的Rectangle,否则两者无法协同工作。不用多说,继承关系可以很好的满足这样的需求。即,动态链接库中的Rectangle应是客户代码中的Rectangle的派生类。如下图所示:
 

 
图1-1 接口/实现分离的组件结构
 
 
这样的结构通常被称为接口/实现分离。接口类通常是只含有纯虚函数(方法)的抽象基类(在C#、Java这些语言中,直接就设置了interface关键字用以指明接口类)。实现类则以具体的代码来实现接口类中确立的多个虚函数。我们在后文还将更多地讲述接口/实现分离结构的好处。通常来讲,组件需要采取这样的结构。
 
说到这里,必须指出目前实际上存在两类组件系统:其中的一类符合接口/实现分离的结构,另一类却并没有这样的强制要求。后一类组件的存在是由于Java和.NET Framework这些新型语言平台引入了从二进制代码块中动态读取类型信息的技术Reflection。这种技术的出现使得客户代码可以不借助接口类而是根据字符串使用动态链接库中的实现类。JavaBean便是这样的组件系统。但是,使用字符串而不是接口类不仅使用上不方便,也不具备型别安全性。事实上,这类组件系统更注重的是单个实现本身的复用(可以称为实现复用),而不能很好地支持相同接口的多个不同实现的切换(这可以称为接口复用)。而基于接口/实现结构的组件系统则对两者都可以很好地支持。BOS和我们以后的讨论都是针对接口/实现结构类型的组件。
 
使用组件之后,我们可以有效地解决问题列表中的1至3。但是,如果将想像稍作深入,我们会发现事情还能做得更好。 待续
本文为BOS(Basic Object System)相关文档。BOS是一个通用软件框架。BOS源码可在下面链接下载:
http://sourceforge.net/projects/bos-code
原创粉丝点击