C和C++语言是流行和常用的依赖于顺序的语言。C++略为有点改进,在类范围内部不依赖于顺序。依赖于顺序是一种惩罚,是一种不重视程序员感受的表现,并且在深层次要求采用采用声明和实现分开,因而也就不能维持DRY原则。违背DRY会导致维护复杂。
我详细说明一下为什么依赖顺序是罪恶的。
大家都知道抽象是一种强大的能力,但是,依赖于顺序强迫我们只能从低层次的细节开始,这跟现如今流行的IDE的智能完成一样。我们不得不把所有的细节描述完备,才能构造更大的构件。
我还是用一个实例来说明吧。
我为了练习的目的,试图写一个HTTP Client,自然,我是从阅读RFC规范开始的。通过扫描RFC的目录和快速掠过其内容,我们发现HTTP是一个请求响应模式的简单协议,其请求和响应都是所谓的Message,显然,只要我们能解析和构造这个Message,我们就大致完成任务。
于是就有了:
namespace Http
{
class Message
{
public:
MessageHeader headers;
CRLF crlf;
MessageBody body;
std::string toString(void)
{
return headers.toString() + crlf.toString() + body.toString();
}
};
class MessageHeader
{
public:
std::vector<Field> fields;
std::string toString(void)
{
std::string result;
for (int i = 0; i < fields.size(); ++i)
{
result += fields[i].toString();
}
return result;
}
};
class Field
{
public:
FieldName name;
Colon colon;
FieldValue value;
CRLF crlf;
std::string toString(void)
{
return name.toString() + colon.toString() + value.toString() + crlf.toString();
}
};
class FieldName
{
public:
FieldNameValue name;
std::string toString(void)
{
std::string result;
switch (name)
{
case CacheControl:
result = "Cache-Control";
break;
......
}
return result;
}
FieldNameValue fromString(std::string fieldName)
{
FieldNameValue result;
if (fieldName == "Cache-Control")
result = CacheControl;
else if (...)
...
return result;
}
};
enum FieldNameValue
{
//generic headers
CacheControl,
Connection,
Date,
Pragma,
......
}
我想,上面的代码非常的直接了当。几乎不需要解释,类似于递归下降法制作解析器,而事实上,确实是下降的,但是递归没有,因为HTTP的Message格式还没有那么强大和灵活。稍微有点奇特的是FieldName和FieldNameValue这一对,它们合作构成了我设想的枚举,这也说明了C/C++语言的枚举并不强大到可以作为一个抽象机制使用,当然,这不是我们焦点。暂且不论。
由于其中的toString都是很简单的,所以我准备把它们做成内联函数,这个无可厚非,我们看看就是这个要求是怎么导致我们需要扭曲我们的代码的。
首先需要说明的是:上面的代码不能通过C++编译器的编译。原因很简单:编译器看到MessageHeader被使用的时候还没有发现其定义,会给出一大堆抱怨。怎么办?我们给出一个前向声明吧。也很简单,在使用之前声明一下就行了:class MessageHeader。把它加在使用之前的任意地方,编译器就应该不说话了吧。
哦,还是不行,原因是:对了,它要求必须有完整的类定义才能定义该类的对象,别忘了,C++是值语义的语言。好吧,我们改造成指针……,类型后面加上*,一连串的.变成->,应该没有问题了吧(如果你做过实际的项目,你就知道:这个修改的代价是非常高的,不过区分指针和原始对象不是我们批判的焦点,这里不再深入)。啊……还是不行,原因仍然很简单,就算可以定义指针而不是真正的对象,我们仍然不不能够调用该指针对象的方法,原因是:我(编译器)没有看到类型的定义,怎么知道有没有该方法可以调用呢?
现在我们的选择有三个,但是都不够优雅。
一、我们调整类型定义的顺序,把低层次的构件往前挪。
二、我们把成员函数的实现挪到最后,这时候它已经看到所有的定义了。
三、这是二的彻底化,干脆移动其实现到另一个文件。
方法一的缺陷就是,我们不再能够自然的从上而下看出我们设计的脉络。这跟我看HTTP的RFC一个缺陷是一样的,HTTP的RFC就不是采用自上而下描述的,而是先描述了一些基础的构件,在描述了整体结构,看得人晕头转向,另外,协议中有大量的用途和功能分析,我不是说这不好,但是作为协议这让人分神。可以把功能或者需求放在开始诱导大家构思HTTP应该怎样设计。而不是放在HTTP协议都描述了以后才描述功能。
方法二和方法三本质上是一样的,但是方法三的可维护性就更差一些了,因为所有的修改都得涉及到两个文件,而且,方法三不再能够是inline的,这个代价让我觉得非常不快。而方法二确实仍保持inline,但是,一旦修改我还是得从头到尾的查找。让我写两遍相同的函数头也让我不爽。
而最本质的原因就是:我没有办法让一个决策(函数签名)在一个地方说明,必须出现在两个地方,这是对DRY或者叫做SPOT原则的公然违反,它们会导致大量的微妙的问题和产生难以维护的代码。
C++在很大程度上鼓励我们采用第三种方案,这也是C++中的正统方案,只要我们坚持顺序依赖,我们就不得不倾向于这种方案。顺序依赖把维持依赖关系的责任推给了程序员而不是编译器自己去解决,这当然简化了编译器,但是却使得程序员的日子更艰难。那么,顺序依赖究竟应该是谁的责任呢?是该由程序员处理的呢,还是该由编译器处理的?
我们先大而化之描述一下依赖。依赖表示一个构件(这儿的构件包括函数,类,全局变量,源文件等等任意可以用来搭建整个系统的部分)对别的构件的使用。我们很自然很直观的能感受到,只要我们使用了别的构件,编译器显然知道我们的构件依赖于那些使用的构件,所以,让程序员维持依赖是不必要的。
但是还有一个问题:构件A依赖于构件B,这个编译器知道,但是构件B在哪儿?这个编译器不知道。显然,我们需要而且也只需要在某个地方描述一下这个构件跟位置的映射,我们的编译器就可以工作了。如果我们把这种映射放在任何构件里面,这样势必会导致这种映射关系描述上的重复,导致大量的维护问题。显然,最适合描述构件跟位置的映射关系的地方是工程的配置文件。它是整个工程级别的属性。
那么顺序依赖是什么呢?顺序依赖本质上是希望把构件和位置的映射关系消除。它要求所有依赖的东西必须在它之前出现,这样,被依赖方也就自然不需要指定位置了,它就在前面。这个有利于分块编译(不知道大家注意没有,分块编译的用处似乎并不是很大,无论是大工程还是小工程,因为:毕竟编译只是一个阶段,而且维持构件是否是最新编译版本似乎也一点都不难),但是实现分块编译导致需要大量的重复编译,毕竟任何被多次依赖的组件都必须多次被编译。并且分块编译要求类似于Makefile之类的支持环境,现代的语言及其开发环境都完全不支持分块编译了,它们都采用运行时动态装载这个更灵活的模式。
分享到:
相关推荐
《罪恶装备》是一款在全球范围内享有极高声誉的格斗类电子游戏,以其独特的角色设计、精美的视觉效果和深度的战斗系统深受玩家喜爱。在这个特定的压缩包中,我们聚焦于其中的一个标志性角色——Sol,他的3D模型是...
侠盗猎车手3罪恶都市《侠盗猎车手III》,罪恶都市【源码】 re3:《侠盗猎车手III》,罪恶都市-源码 介绍 在此存储库中,您将找到GTA III(分支)和GTA VC(分支)的完全相反的源代码。 它已经过测试,可以在Windows...
win7的桌面主题下载,罪恶王冠系列
《罪恶都市修改器》是一款针对著名游戏《侠盗猎车手:罪恶都市》(Grand Theft Auto: Vice City)的第三方工具,旨在为玩家提供更丰富的游戏体验。这款修改器通常包含了各种功能,比如无限生命、无限弹药、快速移动...
这篇论文主要探讨了幸福感与罪恶感这两个人类情感如何在事业关联营销中发挥关键作用。在当前的商业环境中,企业越来越重视与消费者的情感连接,而消费者的主观感受,如幸福感和罪恶感,可能显著影响他们对品牌和产品...
侠盗飞车罪恶都市秘籍大全(完美版) 本文档提供了侠盗飞车罪恶都市秘籍大全(完美版)的游戏指南和秘籍大全。该游戏是 Rockstar Games出品的开放世界动作冒险游戏,于2002年发行。游戏的故事背景设定在虚拟城市...
罪恶都市辅助源码 主要涉及易语言模拟按键编辑秘籍 不懂的小伙伴可以看看哦
画皮下的罪恶
【罪恶王冠电脑图标下载】这一资源主要包含的是与热门动画《罪恶王冠》相关的电脑图标,供用户下载使用。这些图标可能是由粉丝创作或官方发布,旨在为计算机用户提供一种个性化桌面的方式,使他们能够将自己的电脑...
油画中,那私密的罪恶
标题“幸福感与罪恶感双因素对事业关联营销的影响研究”揭示了一个关键的课题:如何利用消费者的幸福感和罪恶感来推动事业关联营销的效果。这篇研究文档深入探讨了这两种情感在市场营销中的角色以及它们如何相互作用...
资本主义的罪恶本质定义.pdf
根据提供的文档信息,本文主要围绕一款名为“有一种王冠,加冕于罪恶”的互动式游戏进行观后感和心得体会的分享。以下是对该游戏中所提及的一些关键元素和设计思路的详细解析: ### 封面设计 - **设计风格**:文档...
侠盗猎车罪恶都市秘籍大全.docx
中国皇权专制赋税制的罪恶.docx
七宗罪影评—人性罪恶的剖析.doc
"《GTAVC》十周年纪念版主线任务通关图文攻略" 这篇攻略文章旨在指导玩家完成《GTAVC》十周年纪念版的主线任务。游戏开始于1986年,Tommy受Sonny派遣来到VC,然而,Tommy遭到袭击,VCS主角维克托与另一人被当场击毙...
尽可能通过参数传递状态,减少全局状态的依赖。 7. **不使用注释和文档字符串**: 注释和文档字符串能帮助其他人理解代码的功能和用法。为函数、类和模块添加清晰的文档字符串,保持良好的注释习惯。 8. **不遵循...
[但是我们有罪恶感]但是我们坚信通过我们的努力.docx