【IT168 技术文章】 2月21日,项目正式开始第二天。
依照昨天设计的框架和接口,我们开始实现这些功能。不过似乎大家的进展都比较慢,特别是XophiiX,似乎陷入了困境之中。
具体是什么问题呢?请看下面的接口定义:
class CReader
{
// ...
public:
template <class ForwardIterator, class InsertIterator>
bool ReadFile(ForwardIterator &input, InsertIterator &output);
};
为了让ReadFile尽量通用,并且完全不依赖于输入和输出的具体介质,我们把ReadFile设计成模板方法,入口参数是输入和输出的iterator。实际上,input接受的是istream_iterator<char>,output接受的是一个deque的inserter。这件事本身应该还不算艰深,只是XophiiX原来并没有关注过iterator的用法,不了解inserter的重大作用,也不清楚模板的成员方法如何实现,使得他实现这个接口时非常郁闷,只好慢慢地翻C++ Primer。
面对这种情况,我也比较矛盾。的确,我可以采用不同的、更简单的方式实现这个接口,例如直接传ifstream和deque,但是扩展性呢?我昨天才说要在设计之初降低耦合,怎么能够走回头路呢?所以我坚持了,而且主动帮他补课。但我知道,要很好地理解这些事情还需要时间。
无论如何,他似乎已经对上面这些技术问题有所了解,不过新的麻烦又来了。由于这个函数要实现的是分词,所以我们必须读取单个的字符,包括空格符和回车符,但是很可惜,用istream_iterator<char>得到的iterator并不能得到空格符和回车符,我们试了很多方法都不行。这回真的郁闷了。怎么办?如果要坚持这种设计,可以考虑设计一种调用ifstream的get方法的iterator。这个方法可行但似乎小题大做,毕竟我并没有义务去扩展STL。麻烦麻烦麻烦!
接下来的时间里我一直在权衡,说实话,我不想放弃这种“完全没有耦合”的完美设计,但又不想在系统设计之初就开始构建自己的基础库,这似乎是一种经典的过度设计。不知不觉一天就这样过去!
回去的路上我好好地总结了一下自己的想法。我突然意识到一个严重的问题:ReadFile接口设计本身是不是就是一种过度设计?我真的需要如此之低的耦合度吗?是的,我其实不需要这样复杂的设计。在可以预见的未来(甚至一直到项目结束),我们读取的介质都只是文件,ifstream其实完全足够。至于输出介质,我可以封装一个CWordQueue(直接把deque<string>重新定义一次),让CReader和作坊工厂都依赖于CWordQueue,未来就算要进行某种扩展,只需要在保证push_back意义不变的前提下重新实现CWordQueue就可以。
那么,新的接口可以这样表示:
class CReade
{
// ...
public:
bool ReadFile(istream &input, CWordQueue &output);
};
我相信这个接口应该可以稳定到项目结束,其实已经足够了。明天我一定要为自己的过度设计向大家道歉,如果不是因为这个烦人的接口,XophiiX也不会那么郁闷。过度设计,让它离的更远些吧。
