- 论坛徽章:
- 0
|
koenig在C++沉思录中说他一般不提OOP,因为他的OOP是指使用继承和动态绑定的编程方法。
但OOP这个东西我认为是思想,无论你使用语言是否有继承,封装,多态的特性支持,你依然可以抽象后面向对象。
一个反例,来自C++沉思录序幕,koenig大师比较了什么是C++可以做好而C做不好的,C++和C作为所有程序员和编程爱好者都有了解的语言,我觉得是最好的范例。
大师写了一个简单的类,代码如下所示:
#include <stdio.h>
class Trace
{
public:
Trace() { noisy = 0; f = stdout;}
Trace (FILE* ff) { noisy = 0; f = ff; }
void print(char* s)
{
if(noisy)
fprintf(f, "%s" , s);
}
void on() {noisy = 1;}
void off() {noisy = 0;}
private:
int noisy;
FILE *f;
};
我们可以发现,一段简单的跟踪实现,包含了两个特性:可以打开或关闭跟踪输出,可以打印到控制台以外的输出设备上,比如文件。
大师给出了一个典型的C语言实现:
#include <stdio.h>
static int noisy = 1;
void trace(char* s)
{
if(noisy)
printf("%s\n",s);
}
void trace_on()
{
noisy = 1;
}
void trace_off()
{
noisy = 0;
}
很好,典型的C风格解决方案,第一个特性完美解决,但问题来了,如果我们想添加一个其他的输出方式,比如C++例子中的文件输出,应该对这些代码进行多大的改动呢?
在我想来典型的解决方案大概是这样的:
#include <stdio.h>
#include <stdlib.h>
static int noisy = 1;
enum OUT
{
StdOut=0,
File=1
};
void file_print(const char* s)
{
FILE* pFile;
pFile = fopen ( "myfile.txt" , "wb" );
fwrite (s , sizeof(s), 1 , pFile);
fclose (pFile);
}
void trace(const char* s,const enum Out out)
{
if(noisy)
{
switch(out)
{
case StdOut:
printf("%s",s);
break;
case File:
file_print(s);
break;
default:
break;
}
}
}
void trace_on()
{
noisy = 1;
}
void trace_off()
{
noisy = 0;
}
嘛,看起来就不那么优雅了,但的确解决了功能问题。如果想要指定一个文件名,我们还需要加一个参数,或者加一个全局变量;
仔细对比,如果不关注 file_print 函数中开关文件具体的实现的话,我们干了什么事情?
为trace函数添加了一个参数,增加了trace函数中的判断。
如果我们main函数中正好使用file_print在向某一个文件输出,那么我们还可以随意使用trace_off切换开关状态嘛?如果我们想切换至标准输出呢?
于是我们就需要实现一个专门的trace_out函数用来切换输出状态,与此同时file_print也要加上循环检测,判断某参数表示的当前使用何种方式输出。
对于这一点,koenig大师的总结是,使用C带来的这些麻烦的根源在于,添加最开始情况下不需要的状态信息非常麻烦.......
但是我觉得麻烦的是手段(代码改动大),但如果思路清晰的话,使用函数指针,可变长数组等方法依然可以让你用面向对象的方法写程序,而不是把业务仅仅视作一个个逻辑过程,这样对于业务变动,会越来越难以维护. |
|