目前我正在阅读这本书" Code Complete,2nd Edition"。在第6.2章中,作者讨论了类和接口,并给出了以下建议:
在可能的情况下使接口具有编程性而非语义性
每个界面由程序部分和语义部分组成。 程序化部分由数据类型和其他属性组成 可以由编译器强制执行的接口。语义 界面的一部分包括关于如何的假设 将使用接口,编译器无法强制执行。该 语义界面包括诸如“常规必须”之类的考虑因素 在RoutineB之前调用“或”如果dataMember1不是,则RoutineA将崩溃 在传递给RoutineA之前初始化。“语义界面 应记录在注释中,但尽量保持接口最小化 取决于文件。接口的任何方面都不能 编译器强制执行的是可能被滥用的方面。 寻找将语义界面元素转换为程序化的方法 通过使用Asserts或其他技术来界面元素。
我理解作者的语义和程序化意味着什么,但我不明白你如何将语义界面功能转换为程序功能。他提到使用断言或其他技术来实现这一目标。
让我们以作者为例:
必须在RoutineB之前调用RoutineA。我假设这些例程是接口(公共函数)的一部分,因为这就是这个丑陋的接口。
因此,如果在调用RoutineB之前确实必须调用RoutineA,那么如何使用断言或其他技术重新组织此接口呢?
我对此有一些想法,但我不确定它们是否正确。
假设RoutineA和RoutineB都是公共函数,这意味着它们都应该彼此独立可用,但唯一的限制是你首先必须调用RoutineA才能独立调用RoutineB。
如果情况确实如此,那么如何使用断言或其他技术解决这个问题?
如果我的假设有错误,请随时纠正我。
另外,我故意在当前标签下发布这个标签,因为像面向对象编程/设计/界面这样的标签只有很少的点击量,这意味着我的问题可能不会被看到很多。
答案 0 :(得分:4)
两个可能的答案:
assert( a_called );
放入例程RoutineB
RoutineB
,并使RoutineA
返回一个具有RoutineB
成员的新对象。在后一种情况下,您可能希望使外部类成为指向内部类的智能指针的瘦包装器,然后RoutineA
将只复制指针。
class Impl;
class SecondClass;
class FirstClass
{
std::shared_ptr<Impl> pimpl;
public:
FirstClass();
SecondClass RoutineA(...);
...
}
class SecondClass
{
std::shared_ptr<Impl> pimpl;
friend class FirstClass;
SecondClass(const std::shared_ptr<Impl>& impl) : pimpl(impl);
public:
void RoutineB(....);
}
SecondClass FirstClass::RoutineA(...)
{
// Do stuff
return SecondClass(pimpl);
}
您也可以使用unique_ptr
执行此操作,但该代码有点长。
答案 1 :(得分:1)
我认为您应该在运行时检查限制(“RoutineA必须在RoutineB之前调用”)。标签中的编程语言无法在编译时检查此类限制。 您的代码可能如下所示:
RoutineA()
{
aCalled = true;
//some operations..
}
RoutineB()
{
if(!aCalled) // or an assertion
{
throw NotReadyException("RoutineA must be called");
}
//some operations..
}