一:把C++当作带C++语法的C使用。 如果你仅仅把C++当作C来使用,那么,请直接使用C。否则含有C++语法的C程序不但不能从C++中得到任何好处,反而会因为C++的种种陷阱让程序还不如直接用C来写简单和不易出错。 1:把成员公有化,类当作带成员函数的结构体使用。 成员暴露,到处赋值,再加上下面几点的因素综合起来,使程序混乱和容易出错,修改也比用C写的代码更为困难。 2:残废的构造函数。 除了构造函数之外,又有一个Init之类的函数用于初始化,导致初始化语义含混不清。典型的用C的思想来写C++。 记住,构造即初始化。 3:包罗万象的超级类。 什么东西都放在一个超级类里面。越来越膨胀。相当于什么都是全局变量。可能用C来写反而不需要那么多全局变量。 4:滥用派生,把派生当成简单的代码重用。 派生关系缺乏设计上的考虑,看到可以重用代码就进行派生,然后成员和虚函数到处乱放。基类的成员放在派生类里面操作,派生类的操作又放在基类里面写成虚函数。这种问题也是把C++当作C来用,认为C++就是一堆数据和函数,派生仅仅是一种语法上的方便,完全无视面向对象方法的规范。结果造成种种混乱,还不如直接用C写来的简单。 派生本来是为了“高内聚低耦合”的设计理念而生的,滥用派生的结果适得其反。其实很多派生都是不必要的,可以简单的实现为成员。 谨记,friend代表最高的耦合,派生其次,成员再其次,引用耦合度最低。
二:过于依赖C++语法,是设计缺陷的表现。 有一个“需求”如下: “简单说,我有几个接口是一层层继承下来的,唤作 iA iB 。iA 是基类,iB 继承至 iA 。然后,我写了一个 cA 类,实现了 iA 接口;接下来我希望再写一个 cB 类,实现 iB 接口。但是,iB 接口的基类 iA 部分,希望复用已经写好的 cA 类。” 用虚继承可以简单的满足这个需求: #define interface struct interface iA { virtual void foo() = 0; }; interface iB : virtual public iA { virtual void bar() = 0; }; class cA : virtual public iA { virtual void foo(); // etc... }; class cB : virtual cA , virtual public iB { virtual void bar(); // etc... }; 其实要实现上述需求大可不必采用“虚继承”之类的“重量级”语法。只需稍作改变。 // 去掉虚继承 interface iA { virtual void foo() = 0; }; interface iB : public iA { virtual void bar() = 0; }; class cA : public iA { virtual void foo(); // etc... }; // 继承改成员 class cB : public iB { public: virtual void foo(){mA->foo();} public: virtual void bar(); // etc... private: cA* mA; }; 这实际上就是一个Decorator。 甚至我们可以更进一步探讨这种“需求”是否适当,是否可以有其他更简单的“需求”。 其实很多这类问题,完全可以从设计上解决,根本无需过于依赖语法。
三:过度设计。 这就走向了前两个错误的另一个极端。虽然本人常常见到的都是前两个错误,但是过度设计也不可不防。
简单,常常是我们写程序追求的目标。面对同样的需求,C++有“多种”实现方法,于是C++“把简单的问题复杂化”。于是,就容易产生上面所说的种种问题。 殊不知,貌似“多种”的实现方法在具体应用场景下也有好有坏,有合适有不合适,如何从中选择,是设计者的事情,而不是语言本身的问题。那些用简单的C写成的程序,有些技术含量之高,显然并不那么“简单”。
|