在设计类的接口时,我通常会有两种想法,我是否应该提供可以通过使用其他成员函数的组合来计算/派生的成员函数。例如:
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;
};
我应该将它作为类成员函数提供,还是作为我可以定义此方法的命名空间?哪一个更受欢迎?
答案 0 :(得分:4)
我认为将getSelectedDocs作为成员函数很好。对于DocContainer来说,这是一个非常合理的操作,因此作为成员是有意义的。成员函数应该在那里使类有用。他们不需要满足某种最低要求。
将它移到课堂外的一个缺点是,当试图弄清楚如何使用DocContainer时,人们将不得不查看两个地方:他们需要查看类和实用程序命名空间。
答案 1 :(得分:4)
一般来说,您可能更喜欢免费功能。从OOP的角度考虑它。
如果该功能不需要访问任何私有成员,那么为什么给予访问它们?这对封装来说并不好。这意味着当修改类的内部时可能会失败的更多代码。
它还限制了可能的代码重用量。
如果您按照以下方式编写函数:
template <typename T>
bool getSelectedDocs(T& container, std::vector<Doc*>&);
然后,getSelectedDocs的相同实现将适用于公开所需函数的任何类,而不仅仅是您的DocContainer。
当然,如果你不喜欢模板,可以使用一个接口,然后它仍适用于任何实现这个接口的类。
另一方面,如果它是一个成员函数,那么它只适用于这个特定的类(以及可能的派生类)。
C ++标准库遵循相同的方法。例如,考虑std::find
,出于这个确切的原因,它是一个自由函数。它不需要知道它正在搜索的类的内部。它只需要满足其要求的一些实现。这意味着相同的find()
实现可以在任何容器,标准库或其他地方使用。
Scott Meyers争辩same thing。
如果你不喜欢它混乱你的主命名空间,你当然可以把它放到一个单独的命名空间中,它具有这个特定类的功能。
答案 2 :(得分:3)
STL基本上针对小型接口,因此在您的情况下,当且仅当getSelectedDocs
可以比isDocSelected
和getDoc
的组合更有效地实施时,它将被实现作为会员职能。
此技术可能无法在任何地方应用,但防止接口混乱是一个很好的规则。
答案 3 :(得分:2)
我同意Konrad和jalf的答案。除非“getSelectedDocs”有显着的好处,否则它会使DocContainer的界面变得混乱。
添加此成员会触发我的smelly code传感器。 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”类。
答案 4 :(得分:0)
答案可能是“它取决于”......
如果该类是许多不同调用者将使用的库的公共接口的一部分,那么提供大量功能以使其易于使用(包括一些复制和/或交叉)是一个很好的论据。但是,如果该类仅由单个上游调用者使用,则提供多种方法来实现相同的操作可能没有意义。请记住,界面中的所有代码都必须经过测试和记录,因此添加最后一点功能总是有成本。
答案 5 :(得分:0)
我认为如果方法:
,这是完全有效的如果该方法包含复杂的逻辑/计算,在许多地方维护而不仅仅是在类中,则尤其如此。