使程序界面编程而不是语义是什么意思?

时间:2015-12-11 13:45:08

标签: java c# android c++

目前我正在阅读这本书" Code Complete,2nd Edition"。在第6.2章中,作者讨论了类和接口,并给出了以下建议:

  

在可能的情况下使接口具有编程性而非语义性
  每个界面由程序部分和语义部分组成。   程序化部分由数据类型和其他属性组成   可以由编译器强制执行的接口。语义   界面的一部分包括关于如何的假设   将使用接口,编译器无法强制执行。该   语义界面包括诸如“常规必须”之类的考虑因素   在RoutineB之前调用“或”如果dataMember1不是,则RoutineA将崩溃   在传递给RoutineA之前初始化。“语义界面   应记录在注释中,但尽量保持接口最小化   取决于文件。接口的任何方面都不能   编译器强制执行的是可能被滥用的方面。   寻找将语义界面元素转换为程序化的方法   通过使用Asserts或其他技术来界面元素。

我理解作者的语义和程序化意味着什么,但我不明白你如何将语义界面功能转换为程序功能。他提到使用断言或其他技术来实现这一目标。

让我们以作者为例:

必须在RoutineB之前调用RoutineA。我假设这些例程是接口(公共函数)的一部分,因为这就是这个丑陋的接口。

因此,如果在调用RoutineB之前确实必须调用RoutineA,那么如何使用断言或其他技术重新组织此接口呢?

我对此有一些想法,但我不确定它们是否正确。

假设RoutineA和RoutineB都是公共函数,这意味着它们都应该彼此独立可用,但唯一的限制是你首先必须调用RoutineA才能独立调用RoutineB。

如果情况确实如此,那么如何使用断言或其他技术解决这个问题?

如果我的假设有错误,请随时纠正我。

另外,我故意在当前标签下发布这个标签,因为像面向对象编程/设计/界面这样的标签只有很少的点击量,这意味着我的问题可能不会被看到很多。

2 个答案:

答案 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..
}