哪个更好:
class Owner
{
public:
void SomeMethodA()
{
_ownee.SomeMethodA();
}
int SomeMethodB()
{
return _ownee.SomeMethodB();
}
private:
Ownee _ownee;
};
或者这个:
class Ownee
{
public:
void SomeMethodA();
int SomeMethodB();
};
class Owner
{
public:
Ownee& GetOwnee() const
{
return _ownee;
}
private:
Ownee _ownee;
};
我似乎记得很久以前读到第一种选择比第二种选择更好,但我不记得为什么了。我想说是因为所有者和所有者的用户之间的耦合较少。客户只需要了解业主的接口,而不需要了解业主,而在第二种选择中,客户需要了解业主和业主的接口。
一个人并非在每一方面、每一种情况下都高人一等。然而,包裹界面和封装构件(第一个)通常是优选。
一个类和它的成员有着特殊的关系。类通常使用其接口来限制功能在其预期的域内操作,并且它可以用于进行额外的内部状态验证和提高程序的正确性。
如果封装良好,Owner
就可以根据需要更自由地更改其实现或成员。
暴露成员通常会导致更高的耦合,并最终将大量长期维护推到客户端上(导致更多的bug和客户端的冗余实现)。一个相当明显的例子是:假设Owner
有两个成员,线程安全必须得到保证——将这一责任推给客户端,并期望客户端在几年内的几次更改中实现和维护线程安全,这是没有意义的。然而,Owner
可以抽象所有这些,并在其实现更改时适当地更改其实现,或者防止对成员(Ownee
)的更改在这方面影响客户端。
当客户端的成员未公开时,对类(Owner
或Ownee
)的更改通常对客户端的影响要小得多,并且类(Owner
)为其功能提供了严格、简单的接口。
"性能"通常被认为是青睐第二个("只为客户端提供访问者")的原因。我认为这是毫无根据的过度简化。性能可能更好,也可能更差!在开发非平凡的程序时,它依赖于C++中的许多东西。再次使用锁定示例:第一个和/或支持公共访问器的坏接口可能需要更多的锁定。同样,优化器具有信息及其成员的位置。Owner
可能提供了大量的内部实现和方法——如果这在很大程度上是私有的,并且使用静态调度,那么它可能会产生一组非常小的导出(或者内联,如果你喜欢的话)方法。访问器本身可能没有那么糟糕,但对所访问对象的操作和依赖关系可能会将大量指令和依赖关系推送给客户端,而Owner
的内部实现可能会以一种单一、紧凑、优化的形式表示所有这些。在C++中,当进行优化时,抽象层可能非常便宜(如果做得好,通常什么都没有)。在现实世界中,绩效双向波动,取决于许多变量。
大多数时候,我使用第一种形式。当我使用第二种形式时,它通常带有:
- 私人/内部课程
- 或者作为更大系统的组件(例如,元编程)
读取:
http://en.wikipedia.org/wiki/Law_Of_Demeter
http://en.wikipedia.org/wiki/Cohesion_(计算机科学)
http://en.wikipedia.org/wiki/Coupling_(计算机科学)
http://en.wikipedia.org/wiki/Encapsulation_(面向对象编程)
http://en.wikipedia.org/wiki/Information_hiding
您想到的是Pimpl(指向实现的指针)习语。
一个很好的方法是在头文件中声明实现类,并在.c文件中定义它;这确保了没有人可以通过.h包含来查看详细信息。
.h文件
#ifndef object_INCLUDED
#define object_INCLUDED
#include <boost/scoped_ptr.hpp>
class Object
{
public:
//...
private:
class ObjectImpl;
boost::scoped_ptr<ObjectImpl> impl_;
}
#endif
.c文件
#include "object.h"
//==================================================
// Implementation of ObjectImpl
//==================================================
class Object::ObjectImpl
{
public:
ObjectImpl(int);
int value() const;
private:
int val_;
};
Object::ObjectImpl::ObjectImpl(int val)
: val_(val)
{}
int
Object::ObjectImpl::value() const
{
return val_;
}
//==================================================
// Implementation of Object
//==================================================
Object::Object(int val)
: impl_(new ObjectImpl(val))
{
}