繁体   English   中英

类中派生信息的成员函数

[英]Member functions for derived information in a class

在设计类的接口时,我通常会陷入两种思维中,是否应该提供可以通过使用其他成员函数的组合来计算/派生的成员函数。 例如:

class DocContainer
{
 public:
   Doc* getDoc(int index) const;
   bool isDocSelected(Doc*) const;
   int getDocCount() const;

  //Should this method be here???
  //This method returns the selected documents in the contrainer (in selectedDocs_out)
  void getSelectedDocs(std::vector<Doc*>& selectedDocs_out) const;
};

我应该将它作为类成员函数提供,还是作为我可以定义此方法的命名空间? 哪一个更受欢迎?

我认为将getSelectedDocs作为成员函数很好。 对于DocContainer来说,这是一个非常合理的操作,因此作为成员是有意义的。 成员函数应该在那里使类有用。 他们不需要满足某种最低要求。

将它移到课堂之外的一个缺点是,当试图弄清楚如何使用DocContainer时,人们将不得不查看两个地方:他们需要查看类和实用程序命名空间。

一般来说,您可能更喜欢自由功能。 从OOP的角度考虑它。

如果函数不需要访问任何私有成员,那么为什么要给予对它们的访问? 这对封装来说并不好。 这意味着更多代码可能会在修改类的内部时失败。

它还限制了可能的代码重用量。

如果你写这个函数是这样的:

template <typename T>
bool getSelectedDocs(T& container, std::vector<Doc*>&);

然后,getSelectedDocs的相同实现将适用于任何公开所需函数的类,而不仅仅是您的DocContainer。

当然,如果您不喜欢模板,可以使用接口,然后它仍适用于实现此接口的任何类。

另一方面,如果它是一个成员函数,那么它只适用于这个特定的类(以及可能的派生类)。

C ++标准库遵循相同的方法。 例如,考虑std::find ,出于这个确切的原因,它是一个自由函数。 它不需要知道它正在搜索的类的内部。它只需要一些满足其要求的实现。 这意味着相同的find()实现可以在任何容器,标准库或其他地方使用。

斯科特迈耶斯争辩同样的事情

如果你不喜欢它混乱你的主命名空间,你当然可以把它放到一个单独的命名空间中,该命名空间具有这个特定类的功能。

STL基本上针对小型接口,因此在您的情况下,当且仅当getSelectedDocs可以比isDocSelectedgetDoc的组合更有效地实现时,它将被实现为成员函数。

这种技术可能不适用于任何地方,但是防止接口混乱是一个很好的规则。

我同意Konradjalf的答案。 除非得到“getSelectedDocs”带来显着的好处,否则它会使DocContainer的界面变得混乱。

添加此成员会触发我的臭味代码传感器。 DocContainer显然是一个容器,为什么不使用迭代器扫描单个文档呢?

class DocContainer
{
public:
  iterator begin ();
  iterator end ();

  // ...
  bool isDocSelected (Doc *) const;
};

然后,使用一个函子来创建文档向量,因为它需要:

typedef std::vector <Doc*> DocVector;
class IsDocSelected {
public:
  IsDocSelected (DocContainer const & docs, DocVector & results)
  : docs (docs)
  , results (results)
  {}

  void operator()(Doc & doc) const
  {
    if (docs.isDocSelected (&doc))
    {
      results.push_back (&doc);
    }
  }
private:
  DocContainer const & docs;
  DocVector & results;
};


void foo (DocContainer & docs)
{
  DocVector results;
  std :: for_each (docs.begin ()
    , docs.end ()
    , IsDocSelected (docs, results));
}

这有点冗长(至少在我们有lambdas之前 ),但这种方法的一个优点是特定类型的过滤不与DocContainer类耦合。 将来,如果您需要一个新的“NotSelected”文档列表,则无需将接口更改为DocContainer,您只需编写一个新的“IsDocNotSelected”类。

答案可能是“它取决于”......

如果该类是许多不同调用者将使用的库的公共接口的一部分,那么提供大量功能以使其易于使用(包括一些复制和/或交叉)是一个很好的论据。 但是,如果该类仅由单个上游调用者使用,则提供多种方法来实现相同的操作可能没有意义。 请记住,界面中的所有代码都必须经过测试和记录,因此添加最后一点功能总是有成本的。

我认为如果方法是完全有效的:

  • 适合班级职责
  • 对于一小部分班级客户来说并不太具体(至少20%)

如果该方法包含复杂的逻辑/计算,在许多地方维护比在类中维护更昂贵,则尤其如此。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM