设计模块与接口的重要性
当一个软件功能比较复杂的时候,需要划分成多个模块,模块之间由接口实现功能调用和参数传递。软件功能模块划分,可以实现功能最小化的集中,实现多人开发与分工。接口的合理设计非常重要,是不同模块之间相互交互的桥梁。
B类依赖A类该如何设计?
如果一个类A可以对外提供一些常用服务,B类用到A类中的一个或者多个服务函数,那个该怎么工程实现呢?
这里由一个候选的方案:
(1)在B中获取A的Instance()即A的单例设计;
(2)在B中包含A的指针;
(3)在B中增加几个用到的函数指针;
以上的设计有以下的特点:
(1)A通常只能是单例设计,在使用时候获取Instance()即可;
(2)在初始化时(使用之前)获取A的指针,就可以使用A所有的public函数;
(3)需要在初始化时候,将所有用到的函数都赋值指向A的函数,由多个函数就需要多次一一赋值;
以上设计是B依赖于A,如果在开发过程中,A的开发者并不了解B中对A由依赖,而不慎改动了A的接口函数,那么B模块就必须跟着改动,否则无法正常工作。
以上的设计约束,只能体现在设计文档中,双方约定好被使用的接口,双方都不可以随意改动。
能否在代码设计过程中引入这个约束呢?在此引入基类和派生类的概念,基类BasicA定义了这一类模块必须具备的纯虚函数接口,A继承并实现了这些函数,B类依赖的是BasicA,指针类型是BasicA的类型,调用函数是BasicA中的纯虚函数,在B的初始化阶段将其成员变量BasicA指针指向A类的实例化对象。由于A类模块的开发者不改动BasicA类,所以开发过程中就不会由接口被随意篡改而导致两个模块不匹配的问题。
设计基类BasicA的另外一个好处是,可以用不同的算法和方法实现不同的A1、A2…类,由于这些类继承了同一个基类,所以对外的接口一致,可以在应用模块B的方便的替换。
软件模块工程设计流程
- 分解模块功能,将要实现的复合功能分解成几个单元模块,单元模块功能要紧凑,基本上是被使用的最小单元;
- 根据各单元模块的功能,设计模块之间相互传递的数据,或者调用的函数接口,函数接口中传递的变量类型,这些数据统称为接口数据,在这一步作出详细的设计;
- 模块之间传递的参数明确后,可以由公共的数据头文件定义这些数据,由各不同模块使用,这样可保证不同模块的参数类型互通;
- 每个单元Class都可以设计一个基类,包含必要的数据和接口函数;
- 将公共数据文件和基类提供给各开发者,由此参考设计派生类和总功能类;
- 总类的设计建议:一个相对完整的总功能,建议设计成一个总的类,例如swarm_planner,包含第一步设计出来的基类指针。在程序流程具体的代码实现中,直接调用相应指针的public函数就可以。在初始时,给指针传递具体用到的派生类。