为每一个软件行业的从业人员,无论是开发人员、项目经理、还是测试人员,也要不断适应这个趋势,我认为C++编译只会使我们的工作更简单和更轻松,希望本文能教会你更多东西。
如果这是你购买的库的设计问题,你对它无计可施(除了换一个更好的库),但你可以将你自己的代码组织得更好一些,以求得将修改代码后的重新编译工作降到最少。这样的设计会更好,更有可维护性,因为它们展示了更好的概念上的分离。
看看这个典型的面向对象的C++编译程序例子:
- class Shape {
- public: // 使用Shapes的用户的接口
- virtual void draw() const;
- virtual void rotate(int degrees);
- // ...
- protected: // common data (for implementers of Shapes)
- Point center;
- Color col;
- // ...
- };
- class Circle : public Shape {
- public:
- void draw() const;
- void rotate(int) { }
- // ...
- protected:
- int radius;
- // ...
- };
- class Triangle : public Shape {
- public:
- void draw() const;
- void rotate(int);
- // ...
- protected:
- Point a, b, c;
- // ...
- };
设计思想是,用户通过Shape的public接口来操纵它们,而派生类(例如Circle和Triangle)的实现部分则共享由protected成员表现的那部分实现(implementation)。这不是一件容易的事情:确定哪些实现部分是对所有的派生类都有用的,并将之共享出来。
因此,与public接口相比,protected成员往往要做多得多的改动。举例来说,虽然理论上“中心”(center)对所有的图形都是一个有效的概念,但当你要维护一个三角形的“中心”的时候,是一件非常麻烦的事情——对于三角形,当且仅当它确实被需要的时候,计算这个中心才是有意义的。#t#
protected成员很可能要依赖于实现部分的细节,而Shape的用户(译注:user此处译为用户,指使用Shape类的C++编译,下同)却不见得必须依赖它们。举例来说,很多(大多数?)使用Shape的代码在逻辑上是与“颜色”无关的,但是由于Shape中“颜色”这个定义的存在,却可能需要一堆复杂的头文件,来结合操作系统的颜色概念。
当protected部分发生了改变时,使用Shape的代码必须重新编译——即使只有派生类的实现部分才能够访问protected成员。于是,基类中的“实现相关的信息”(information helpful to implementers)对用户来说变成了象接口一样敏感的东西,它的存在导致了实现部分的不稳定,用户代码的无谓的重编译(当实现部分发生改变时),以及将头文件无节制地包含进用户代码中(因为“实现相关的信息”需要它们)。有时这被称为“脆弱的基类问题”(brittle base class problem)。
一个很明显的解决方案就是,忽略基类中那些象接口一样被使用的“实现相关的信息”。换句话说,使用接口,纯粹的接口。也就是说,用抽象基类的方式来表示接口:
- class Shape {
- public: //使用Shapes的用户的接口
- virtual void draw() const = 0;
- virtual void rotate(int degrees) = 0;
- virtual Point center() const = 0;
- // ...
- // 没有数据
- };
- class Circle : public Shape {
- public:
- void draw() const;
- void rotate(int) { }
- Point center() const { return center; }
- // ...
- protected:
- Point cent;
- Color col;
- int radius;
- // ...
- };
- class Triangle : public Shape {
- public:
- void draw() const;
- void rotate(int);
- Point center() const;
- // ...
- protected:
- Color col;
- Point a, b, c;
- // ...
- };
现在,用户对C++编译与派生类的实现部分的变化之间的关系被隔离了。我曾经见过这种技术使得编译的时间减少了几个数量级。