C++语言实际上是几种不同语言的聚集地,你可以把他看成一种语言,也可以把他看成一种语言,要进行对C++程序地开发,那么复杂性会大幅度地增加,这就是C++在实践中难于控制的一个主要原因。
混合使用不同风格,就好像在一个源文件里混合使用多种不同的语言,复杂和不一致性必然暴露。当然,C++独特的魅力正在于混合风格编程的强大威力。这正是一把双刃剑,虽然具有潜在的强大威力,但是通常来说也是导致项目混乱的重要原因。
我认为以下面的原则进行实际开发,将可以在一定程度上规避风险:
1). 在任何一个单个的时间点,只使用一种编程风格。
2). 以一种风格为主风格,用它来组织整体模块的开发。
3). 在遇到特别适合另一种风格的典型场景,可以用一个子模块包装该场景,然后在该子模块中使用该风格,但记住遵循要求1,避免混合风格。此外,必须通过封装手段将该模块包装起来,以符合主体风格的要求。比如说,主风格是better C,在某个子模块中用到了面向对象,则应当使这个子模块从整体上看来像是一个普通的C过程。
4). 在个别场合,混合风格的确有很大的好处。但是,这种情形是比较少见的,一般来说比较成功的实践已经总结成patterns,所以在工程实际中,可以强行规定,只有在符合某个patterns的情况下才可以谨慎地使用混合风格,严禁擅自创造新的混合用法。
现在来讨论一下究竟应当如何划分C++风格。Stroustrup对C++风格的分类是从语言开发者的角度进行的。如果我们以下面的原则进行分类,我认为会得出不同的结果:
1) 每一种风格必须构成一个完整的子语言,具有完备性,可以单独使用这一子语言开发任何软件系统,有经过历史验证的成功经验。
2) 每一种风格必须相对简单,有一致的、简单的、得到认可和验证的原则。
3) 每一种子语言必须能够在现实世界中找到相对应的其他语言。
依据以上原则,我将C++语言划分为三个半子语言:
1) Better C, 只增加函数重载、引用类型、缺省参数等简单特性的类C子集。对应ANSI C语言。
2) ADT C++,即C with Class,整个程序由平面化的具体类(concrete class)对象构成,无继承,无多态。对应Ada 83语言。
3) IDL C++,我称之为Interface-Oriented,典型范例是COM组件模型。
3.5) GP C++, 利用模板技术形成了一种库和组件的实现语言。这不是一种完整子语言,一方面因为可以把它看成是ADT C++的一种延伸,另一方面它必须依附于其他风格而发挥作用。
显然,我这里遗留了一个最重要的风格,也就是我们通常所说的“传统面向对象”风格,由Smalltalk,Java等语言所展示的。由MFC等类库经过多年实践论证了的一种风格:靠庞大的继承树抽象和组织各种数据类型,靠继承和组合实现代码复用。这种风格为什么没有被我提及呢?
因为我认为这种风格实际上是一种混合风格!可以认为是在试图融合上述第2、3和3.5种风格。在前述的三条原则里,它严重地违背了第二条。由于C++的静态本质,由于C++缺乏天然的类库和垃圾收集机制,使得在C++语言中进行Smalltalk风格的编程非常非常困难,以至于为了克服这些困难,C++语言实际上发展出了一套不同于Smalltalk、Java风格的独特的“面向对象”编程风格。
这套风格历经近15年实践,应该说有成功有失败,虽然出版了大量的著作,至今没有形成简单的、一致的、可仿效的风格指导。从某种意义上说,如此多的C++面向对象编程指导书籍十几年常盛不衰,恰恰说明这种风格的困难程度和难以仿效性。
就我个人而言,我已经不再以这种风格为指导思想了。我不会再拼命地构造继承树,思考哪些函数应该是虚函数这类问题了。你可以认为“为了复用代码而进行的继承”是这种风格的标志。
请注意,ADT C++允许组合,对于继承则应该想尽一切办法避免。而IDL C++的典型代表COM,根本就不支持这种继承,它支持的只是接口的复用。当然,这并不是要否定十几年来C++语言在面向对象方面发展的成绩。但是,如果你现在从头开始规划一个完整的项目,那么我认为如果选择这种杂合风格,是不太明智的。但是这种风格也有两个典型的使用场景:
1) 有一个完整的框架支持。比如MFC。虽然这种风格本身有很多技术难点,但是MFC这样的框架已经帮你克服了一部分,给你营造了一个类似Smalltalk那样的、相对舒适环境,这时候可以使用这种风格。但是通常要认识到,这类框架在克服不少技术难点的同时,引入了一些新的问题,有时是更加难以对付的问题,所以要明智,并且做好充分准备。
2) 符合经典模式。如果遇到某个典型的“面向对象”场景,已经有了成熟的、优秀的、现成的、文档化了的设计解决方案,则可以有选择的、谨慎地使用之。我指的主要就是GoF和其他一些设计模
这里所谓的“经典模式”数量绝对不会太多,但是却大量地、反复地出现在设计中,并且往往复合出现。这样的情况用已经经过验证的设计方案来解决是非常合适我个人在这里有一些实践,觉得应该注意几个问题。***是要谨慎,我遇到过大量的情形,看上去很适合用某个模式来解决,但是真的用了才发现并不是这么回
在不适合的地方套用了错误的模式,会把事情弄得一团糟;二是***将设计方案局部化,包装起来,从外面看不出你使用了什么模式。三是注意内存问题。使用OO风格的***障碍其实就是内存问题。
【编辑推荐】