2

我将抽象基类定义为 API 的一部分,供其他开发人员使用,有点像定义 Android API 并让开发人员使用它来制作手机应用程序。API 的创建者(即我)何时应该提供某些功能,以及创建者何时应该将其留给开发人员(即 API 的用户)来定义该功能?

这是一个相关的示例:假设我正在定义MyObj的通用树(用户可以在其中创建从MyObj派生的类并具有子类和父类,并且可以通过类型和值进行过滤。

class MyObj
{
 public:
  // These must be provided
  MyObj const * parent();
  bool setParent(MyObj const * i_obj);

  std::vector<MyObj const * > children() const;
  bool addChild(MyObj const * i_obj);
  bool removeChild(MyObj const * i_obj);


  // The following functions can be implemented
  // by using the functions listed above.
  // Should I provide them (by leaving them in the class or
  // defining them as utility functions, etc), or should I let 
  // the developers define them?
  MyObj const * root() const;
  bool hasChild(MyObj const * i_obj) const;
  std::vector<MyObj const * > descendants() const;
  std::vector<MyObj const * > childrenFiltered(FilterType i_type, FilterValue i_val) const;
  std::vector<MyObj const * > descendantsFiltered(FilterType i_type, FilterValue i_val) const;

 private:
  std::vector<MyObj * > m_children;
  MyObj * m_parent;
};

需要考虑的事项:

  • 如果它涉及使用私有变量,以至于开发人员(API 的用户)自己无法提供该功能,那么我必须提供它。
  • 如果某些功能将被许多开发人员(API 的用户)使用,请自行定义。这样一来,开发人员 A 就不需要开发开发人员 B 开发的相同的一组非常有用的实用功能;他们都会使用我提供的那些。
    • 另一方面,也许最好不要试图预测基类将如何使用,而是提供允许开发人员做任何他们想做的事情所需的最少功能。
  • 如果功能是核心功能,提供它(即使它可以只使用其他公共功能来实现)
    • 确定某事是否是核心功能似乎是一项相当模糊的业务

相关文章:

4

1 回答 1

1

图书馆设计是一项相当复杂的工作,需要考虑许多不同的事情。根据我对这个主题的笔记,我可能会写一本书。上面的问题似乎集中在个别课程上,这就是这个答案的意义所在。如果事情变得更加复杂,例如,如果您有用于类或算法的系统,则在决定什么去哪里显示时会有更多的复杂性。

对于一个类,基本规则其实比较简单:

  1. 班级应该足够了。也就是说,您需要抽象的基本部分以及维护类内部完整性所必需的一切:一旦构造,对象将保持有效状态直到销毁(可能的例外是成员函数仅支持基本异常保证可能会将对象变成仅可破坏的状态;您应该努力不拥有任何这些)。
  2. 课堂应该是高效的。也就是说,如果公开的接口不允许有效实现典型用例,但使用类的内部结构会支持这一点,那么提供相应的功能可能是一个好主意。例如std::list<T>::sort(),可以比std::merge_sort()在暴露的迭代器上使用 eg 更有效。
  3. 班级应该是最小的。不要包含任何使物体变大的东西,因为它会“很好”。
  4. 或者,您可能希望提供本质上的转发功能,使界面变得更好。例如,在一个序列上,您可能希望拥有一个push_back()函数,而不是要求用户使用类似cont.insert(value, cont.end()). 任何写起来很重要但可以用接口完成的东西都不应该是接口的一部分。

在某种程度上,这使您获得了一个非常简单的类。用户可以使用它编写自己的算法。为了避免大量重复,您可能还希望提供可能根据您的类的抽象实现的算法。例如,如果您的类表示对节点的所有子节点运行的树算法应该适用于所有类型的树。例如,std::find()适用于所有序列。仅当容器可以做得更好时,将其添加到容器中才有意义,例如,当您搜索容器的键类型时,关联容器就是这种情况。如果你需要找别的东西std::find()即使是这些,也是您想要的。然而,创建适当的算法库并提出适当的抽象比创建体面的类要大得多。

顺便说一句,您上面的“界面”会让我寻找替代库。如果没有,我会认真考虑是否创建我自己的选项。具有暴露特定容器或指针的接口的 C++ 库在我考虑使用它之前需要添加相当多的值。

于 2012-01-20T21:07:27.397 回答