用 C++ 设计包装器 Class
Design of a Wrapper Class in C++
我必须使用界面非常笨拙的旧 class。因为我不能改变它并且依赖它,所以我想构建一个包装器,提供一个干净的界面。假设我有一个 class ClumsyClass。基本上,我有三种方法:
1.参考会员
Class Wrapper {
public:
Wrapper (ClumsyClass& clumsyClass)
: m_clumsyClass(clumsyClass)
{ }
int getSmth() {
return m_clumsyClass.getSmth();
}
private:
ClumsyClass& m_clumsyClass;
}
2。指针成员
Class Wrapper {
public:
Wrapper (ClumsyClass* clumsyClass)
: m_clumsyClass(clumsyClass)
{ }
int getSmth() {
return m_clumsyClass->getSmth();
}
private:
ClumsyClass* m_clumsyClass;
}
3。继承
Class Wrapper : public ClumsyClass {
...
}
哪种方法是 "cleanest" 实现包装器的方法?我更喜欢第三个,但是当我已经有一个 ClumsyClass 对象然后创建一个 Wrapper 对象(复制构造函数)时,将需要更多内存(因为在我的情况下需要原始 class 的实例)。
我会避免使用 3,因为它无法封装 ClumsyClass
。 Wrapper
的用户可以有意或无意地直接访问 ClumsyClass
的 "clumsy" 界面,这是您试图避免的。更喜欢组合而不是继承。
1. 和 2. 之间的区别很小。使用引用成员会降低包装器的灵活性。 class 不可分配,您不能重新设置引用并将其替换为 ClumsyClass
的不同实例,并且成员不能为空。根据您的要求,这些可能是好事也可能是坏事。
但如评论中所述,默认选择可能应该是将 ClumsyClass
作为 Wrapper
的按值成员:
class Wrapper {
public:
// possible constructors
//Wrapper(const ClumsyClass& cc) : m_clumsyClass(cc) {} // copy
//Wrapper(ClumsyClass&& cc) : m_clumsyClass(std::move(cc)) {} // move
int getSmth() { return m_clumsyClass.getSmth(); }
private:
ClumsyClass m_clumsyClass;
};
在您的特定用例中这可能不可行或不可取的原因有很多,然后您可以回退到选项 1 或 2。决定主要取决于所有权。 Wrapper
"own" 应该 ClumsyClass
还是 ClumsyClass
的实例的生命周期在 Wrapper
之外?
使用直接成员的一个潜在缺点是您不能再将 ClumsyClass
的实现隐藏在前向声明后面,因此您会丢失一些 ClumsyClass
的封装。值得注意的是,解决这个问题的一种方法是提取出Wrapper
继承的抽象基class"interface"。类似于:
class IWrapper {
public:
virtual ~IWrapper() {}
virtual int getSmth() = 0;
};
这可能会提供额外的好处,例如可测试性。
从你提到的限制看来,这可能是一个可行的方法:
使用您喜欢的方法创建接口
struct IMyMethods
{
virtual int getOther() = 0;
...
virtual ~IMyMethods() {};
};
现在创建一个新的 class 派生自您的 class 和您要在 class 上使用的接口,在其中添加接口
的实现
class MyAgileClass : public ClumsyClass, IMyMethods
{
public:
MyAgileClass(/*same args as for ClumsyClass*/)
{}
~MyAgileClass()
{}
virtual int getOther()
{
return getSmth(); // clumsy class function
}
...
};
您现在可以直接或通过简化界面访问 ClumsyClass
我必须使用界面非常笨拙的旧 class。因为我不能改变它并且依赖它,所以我想构建一个包装器,提供一个干净的界面。假设我有一个 class ClumsyClass。基本上,我有三种方法:
1.参考会员
Class Wrapper {
public:
Wrapper (ClumsyClass& clumsyClass)
: m_clumsyClass(clumsyClass)
{ }
int getSmth() {
return m_clumsyClass.getSmth();
}
private:
ClumsyClass& m_clumsyClass;
}
2。指针成员
Class Wrapper {
public:
Wrapper (ClumsyClass* clumsyClass)
: m_clumsyClass(clumsyClass)
{ }
int getSmth() {
return m_clumsyClass->getSmth();
}
private:
ClumsyClass* m_clumsyClass;
}
3。继承
Class Wrapper : public ClumsyClass {
...
}
哪种方法是 "cleanest" 实现包装器的方法?我更喜欢第三个,但是当我已经有一个 ClumsyClass 对象然后创建一个 Wrapper 对象(复制构造函数)时,将需要更多内存(因为在我的情况下需要原始 class 的实例)。
我会避免使用 3,因为它无法封装 ClumsyClass
。 Wrapper
的用户可以有意或无意地直接访问 ClumsyClass
的 "clumsy" 界面,这是您试图避免的。更喜欢组合而不是继承。
1. 和 2. 之间的区别很小。使用引用成员会降低包装器的灵活性。 class 不可分配,您不能重新设置引用并将其替换为 ClumsyClass
的不同实例,并且成员不能为空。根据您的要求,这些可能是好事也可能是坏事。
但如评论中所述,默认选择可能应该是将 ClumsyClass
作为 Wrapper
的按值成员:
class Wrapper {
public:
// possible constructors
//Wrapper(const ClumsyClass& cc) : m_clumsyClass(cc) {} // copy
//Wrapper(ClumsyClass&& cc) : m_clumsyClass(std::move(cc)) {} // move
int getSmth() { return m_clumsyClass.getSmth(); }
private:
ClumsyClass m_clumsyClass;
};
在您的特定用例中这可能不可行或不可取的原因有很多,然后您可以回退到选项 1 或 2。决定主要取决于所有权。 Wrapper
"own" 应该 ClumsyClass
还是 ClumsyClass
的实例的生命周期在 Wrapper
之外?
使用直接成员的一个潜在缺点是您不能再将 ClumsyClass
的实现隐藏在前向声明后面,因此您会丢失一些 ClumsyClass
的封装。值得注意的是,解决这个问题的一种方法是提取出Wrapper
继承的抽象基class"interface"。类似于:
class IWrapper {
public:
virtual ~IWrapper() {}
virtual int getSmth() = 0;
};
这可能会提供额外的好处,例如可测试性。
从你提到的限制看来,这可能是一个可行的方法:
使用您喜欢的方法创建接口
struct IMyMethods
{
virtual int getOther() = 0;
...
virtual ~IMyMethods() {};
};
现在创建一个新的 class 派生自您的 class 和您要在 class 上使用的接口,在其中添加接口
的实现class MyAgileClass : public ClumsyClass, IMyMethods
{
public:
MyAgileClass(/*same args as for ClumsyClass*/)
{}
~MyAgileClass()
{}
virtual int getOther()
{
return getSmth(); // clumsy class function
}
...
};
您现在可以直接或通过简化界面访问 ClumsyClass