客户的需求永远在变,客户永远不知道具体想要什么,只想要好的.
就像买衣服一样,你只想买又便宜又好看的衣服,作为客户的你,其实并知道具体应该怎样设计衣服,却会提出颜色要亮一点,袖子要长一点。如何应变不断变化的需求,从销售到管理有一套方法学, 软件开发方面也有敏捷等方法学,现在我们从代码的角度讲如何应对这种变化。
编程的不二法则 -- 基于接口编程和重构。
举个例子,MP3播放器.
1. 最开始我们想到的:
我们想到的首先是创建一个Player的类:
class CMP3Player
{
public:
CMP3Player();
~CMP3Player();
int Play(const char* fileName);
int Pause();
int Stop();
int GetPos() const;
int SetPos(const int pos);
int GetVolumn() const;
int SetVolumn() const;
.....// 其他的播放器方法
};
然后,就只要在各个方法中调用解码器的函数(对于Win平台有MDC系列可以用).好了,客户需求变了,我现在要放AVI格式的,怎么办?再写一个AVIPlayer吗?
2. 一点改进
所以,我们首先引入一个IPlayer的接口类,然后让MP3Player和AVIPlayer继承它:
class IPlayer
{
public:
virtual ~IPlayer() {}
virtual int Play(const char* fileName) = 0;
virtual int Pause() = 0;
virtual int Stop() = 0;
...
protected:
IPlayer(){//做一些公共的初始化工作}
};
class MP3Player: public IPlayer
{
... // 实现IPlayer的接口
};
class AVIPlayer: publlic IPlayer
{
.... // 实现IPlaye的接口
};
3. 继续改进
看起来差不多了,其实不然,因为你写着写着,你就会发现其实无论是MP3Player还是AVIPlayer的播放逻辑都差不多,越往后写,只是不断在拷贝代码而已,真正区别的地方
只是调用了不同的解码器函数。
于是,我们需要抽取一个解码器类 --- IDecoder,它主要负责封装媒体库,同样它也是一个
接口。
class IDecoder
{
public:
virtual int OpenStream(int handle) = 0;
virtual int CloseStream(int handle) = 0;
virtual int Play(int handle, HWND hWnd) = 0;
virtual int InputData(int handle, void* buf, int bufSize) = 0;
....
};
class MP3Decoder: public IDecoder {...}
class AVIDecoder: public IDecoder {...}
现在我们看看IPlayer应该是什么样子的:
class IPlayer
{
public:
virtual ~IPlayer() = 0;
virtual int Play(const char* fileName) = 0;
virtual int Pause() = 0;
virtual int Stop() = 0;
...
protected:
IPlayer(){//做一些公共的初始化工作}
protected:
IDecoder* m_pDecoder;
};
OK,我们已经不需要什么MP3Player或者AVIPlayer了,我们只需要一个FilePlayer就可以了.
class FilePlayer: public IPlayer {...},它会根据文件类型来创建不同的IDecoder.
4. 满足更多的需求
现在 客户又有需求了,除了播放本地文件外,还要支持实时播放,这时候我们只需要些一个
class VODPlayer: public IPlayer就可以了,需要注意的是,实时播放也有各种各样的协议,
或者是Rtsp,或者是MMST,对了,需要给协议做一个接口
class IProtocol
{
public:
virtual int Start() = 0;
virtual int Stop() = 0;
virtual int OnReceiveData(void* data, int size) = 0; // 一般是回调函数
};
class RtspProtocol: public IProtocol {...}
class MMSTProtocol: public IProtocol {...}
这时候我们的VODPlayer会变成下面的样子:
class VODPlayer: public IPlayer
{
public:
.... // 实现IPlayer的接口
protected:
IProtocol* m_pProtocol;
};
5.一点总结
上 面改进的代码有很多,比如还可以引入Factory和Strategy等设计模式,就是由DecoderManager和PlayerManager来负 责创建和维护各种各样Decoder和 Player;甚至解码器的可以做成动态加载的方式,用插件化进行管理,等等。但是有了一个定义良好的接口,做其他事情都简单。
代码最重要的是正确性。那么,除了正确性以外呢?不是效率,不是性能,而是可扩展性和可
读性;以前设计精良的代码可能现在一团糟,我们需要做的是不断的重构。
现在已经不是K&R的时代(一个人写出C编译器和Unix操作系统)
因篇幅问题不能全部显示,请点此查看更多更全内容