控制反转的利弊

时间:2011-03-01 15:48:58

标签: c++ design-patterns callback iterator inversion-of-control

假设我有一个我想通过API公开的[acme]对象流。我有两个选择,回调和迭代器。

API#1:回调

// API #1
// This function takes a user-defined callback 
// and invokes it for each object in the stream.
template<typename CallbackFunctor>
void ProcessAcmeStream(CallbackFunctor &callback);

API#2:迭代器

// API #2
// Provides the iterator class AcmeStreamIterator.
AcmeStreamIterator my_stream_begin = AcmeStreamIterator::begin();
AcmeStreamIterator my_stream_end   = AcmeStreamIterator::end();

API#1从用户手中获取程序的控制流,并且在整个流消耗之前不会返回(暂时忘记异常)。

API#2保留用户手中的控制流,允许用户自己前进流。

API#1感觉更多更高级别,允许用户立即跳转到业务逻辑(回调函子)。另一方面,API#2感觉更灵活,允许用户进行较低级别的控制。

从设计角度来看,我应该选择哪一个?我还没有看到更多的利弊吗?未来有哪些支持/维护问题?

5 个答案:

答案 0 :(得分:9)

迭代器方法更灵活,回调版本可以通过现有算法轻松实现第一个:

std::for_each( MyStream::begin(), MyStream::end(), callback );

答案 1 :(得分:6)

IMO,第二个显然优越。虽然我可以(有点)理解你的感觉它是较低的水平,但我认为这是不正确的。第一个定义了它自己的“更高级别”的特定概念 - 但它与C ++标准库的其余部分不相符,最终相对难以使用。特别是,它要求如果用户想要的东西等同于标准算法,则必须从头开始重新实现,而不是使用现有代码。

第二个完全适合库的其余部分(假设您正确实现了迭代器),并为用户提供了通过标准算法在更高级别处理数据的机会(和/或者遵循标准模式的新的非标准算法。

答案 2 :(得分:5)

回调函数的一个 回调优势 是API的用户不会搞乱迭代。可以很容易地比较错误的迭代器,或使用错误的比较操作或以其他方式失败。回调API阻止了这一点。

使用回调轻松完成取消枚举,BTW:只需让回调返回bool并继续,只要它返回true即可。

答案 3 :(得分:4)

C ++标准库习惯用法是提供迭代器。如果您提供迭代器,那么ProcessAcmeStreamstd::for_each的简单包装器。也许值得写作的麻烦,也许不是,但它并没有完全将你的调用者提升到一个全新的可用性世界,它是一个新的名称,用于将标准库函数应用到你的迭代器对。

在C ++ 0x中,如果你也通过std::beginstd::end使迭代器对可用,那么调用者可以使用基于范围的for,这将它们带入业务逻辑,就像{{ {1}}确实可能更快。

所以我会说,如果有可能提供一个迭代器然后提供它 - 如果调用者想要以这种方式编程,C ++标准会对你进行控制反转。至少,对于控制就像它一样简单的情况。

答案 4 :(得分:2)

从设计的角度来看,我会说迭代器方法更好,因为它更简单,也更灵活;在没有lambdas的情况下制作回调函数真的很烦人。 (现在C ++ 0x将具有lambda表达式,但这可能不那么令人担忧,但即便如此,迭代器方法也更通用。)

回调的另一个问题是取消。你可以返回一个布尔值来表示你是否想要取消枚举,但是当控件不在我手中时我总是感到不安,因为你并不总是知道会发生什么。迭代器没有这个问题。

当然,总有一个问题是迭代器可以是随机访问而回调不是,因此它们也更具可扩展性。

相关问题