这个问题的答案肯定是主观的,但我希望尽量避免争论。如果人们适当对待它,我认为这可能是一个有趣的问题。
在我的几个最近的项目中,我经常使用实现长委派链的架构。
双重委派链经常会遇到:
bool Exists = Env->FileSystem->FileExists( "foo.txt" );
而三重委托并不罕见:
Env->Renderer->GetCanvas()->TextStr( ... );
高阶委托链存在,但非常少见。
在上述例子中,由于使用的对象始终存在且对程序运行至关重要,在执行开始时已明确构造,因此不执行任何空值运行时检查。基本上,在这些情况下我会将委托链拆分为:
1) 通过委托链重用对象:
{ // make C invisible to the parent scope
clCanvas* C = Env->Renderer->GetCanvas();
C->TextStr( ... );
C->TextStr( ... );
C->TextStr( ... );
}
2)在委托链的中间某个位置应该在使用之前检查中间对象是否为NULL。例如:
clCanvas* C = Env->Renderer->GetCanvas();
if ( C ) C->TextStr( ... );
我曾经通过提供代理对象来处理情况(2),以便在非NULL对象上调用方法,导致结果为空。
我的问题是:
- 情况(1)或(2)中的任何一个是模式还是反模式?
- 有没有更好的方法来处理C++中的长委托链?
以下是我考虑做出选择时考虑的一些利弊:
优点:
- 它非常描述性:从一行代码清楚地知道对象来自哪里
- 长的委托链看起来很好看
缺点:
- 交互式调试很麻烦,因为很难检查委托链中不止一个临时对象
我想知道长委托链的其他利弊。请根据您的推理进行投票,并基于论点的说服力而不是您同意的程度。
Env->Renderer->GetCanvas()->TextStr()
这样的代码时,我需要一次性考虑许多事情:环境对象是什么,渲染器对象是什么,画布是什么,TextStr
方法又是做什么的。此外,这意味着代码与所有这些类紧密耦合在一起,而它可能可以更好地封装(阅读文本从画布中真正需要访问整个环境吗?)。请参阅《迪米特定律》(http://en.wikipedia.org/wiki/Law_of_Demeter) 了解更多信息。 - Luc Touraille