铸造 sibling class 有充分的理由吗?
Casting sibling class with a good reason?
我有一个 Base class 和很多派生的 classes(即 DerivedX)作为生产代码。
在不触及那些 classes 的情况下,我创建了一个从 Base 派生的 BaseExt class,以便为测试目的操纵内部数据。
+-----------+ +-----------+
| Base + <- - - - - | BaseExt |
+-----------+ +-----------+
/|\
|
+-----------+
| DerivedX +
+-----------+
这是代码示例
class Base {
public:
int data() const { return _data; }
protected:
Base() = default;
virtual ~Base() = default;
int _data;
};
class Derived1 : public Base {
};
class BaseExt : public Base {
public:
void inject_data(int data) { _data = data; }
};
它对我来说很直观。
std::shared_ptr<Base> p = std::make_shared<Derived1>();
auto d1 = p->data(); // 0
std::static_pointer_cast<BaseExt, Base>(p)->inject_data(10);
auto d2 = p->data(); // 10
基线:我不想更改我的生产代码(即 Base 和 DerivedX)
当然我可以扩展 Derived1 来做同样的工作,但是我有很多这样的派生 classes,它们添加了太多的代码,只是一个简单的任务。
问题是
- 对于这个用例,转换成同级 class 是否合理?
- 如何保证安全? (例如兄弟中没有属性 class)
- 任何更好更简洁的解决方案(除了修改 Base 和 DereivedX classes)
模板呢?
template<typename T>
struct TestableDerived : T {
inject_data(int data) {
_data = data;
}
static std::shared_ptr<Base> createAndInjectData(int data) {
std::shared_ptr<TestableDerived<T>> ptr = std::make_shared<TestableDerived<T>>();
ptr->inject_data(data);
return ptr;
}
}
或者,如果您准备更改您的来源,使每个派生的 class 实际上继承自 Base
(即 class DerivedX : public virtual Base
),那么我认为您可以将其与多重继承只得到 'an extra method during testing' (我还没有测试过):
struct BaseExt : virtual Base {
void inject_data(int data) { _data = data; }
}
template<typename T>
struct TestDerived<T> : virtual T, virtual BaseExt {}
void doSomeTesting() {
std::shared_ptr<TestDerived<DerivedX>> p1 = std::make_shared<TestDerived<DerivedX>>();
std::shared_ptr<DerivedX> p2 = p1;
std::shared_ptr<BaseExt> p3 = p1;
assert(p1->data()==0);
assert(p2->data()==0);
assert(p3->data()==0);
p3->inject_data(10);
assert(p1->data()==10);
assert(p2->data()==10);
assert(p3->data()==10);
}
我有一个 Base class 和很多派生的 classes(即 DerivedX)作为生产代码。 在不触及那些 classes 的情况下,我创建了一个从 Base 派生的 BaseExt class,以便为测试目的操纵内部数据。
+-----------+ +-----------+
| Base + <- - - - - | BaseExt |
+-----------+ +-----------+
/|\
|
+-----------+
| DerivedX +
+-----------+
这是代码示例
class Base {
public:
int data() const { return _data; }
protected:
Base() = default;
virtual ~Base() = default;
int _data;
};
class Derived1 : public Base {
};
class BaseExt : public Base {
public:
void inject_data(int data) { _data = data; }
};
它对我来说很直观。
std::shared_ptr<Base> p = std::make_shared<Derived1>();
auto d1 = p->data(); // 0
std::static_pointer_cast<BaseExt, Base>(p)->inject_data(10);
auto d2 = p->data(); // 10
基线:我不想更改我的生产代码(即 Base 和 DerivedX)
当然我可以扩展 Derived1 来做同样的工作,但是我有很多这样的派生 classes,它们添加了太多的代码,只是一个简单的任务。
问题是
- 对于这个用例,转换成同级 class 是否合理?
- 如何保证安全? (例如兄弟中没有属性 class)
- 任何更好更简洁的解决方案(除了修改 Base 和 DereivedX classes)
模板呢?
template<typename T>
struct TestableDerived : T {
inject_data(int data) {
_data = data;
}
static std::shared_ptr<Base> createAndInjectData(int data) {
std::shared_ptr<TestableDerived<T>> ptr = std::make_shared<TestableDerived<T>>();
ptr->inject_data(data);
return ptr;
}
}
或者,如果您准备更改您的来源,使每个派生的 class 实际上继承自 Base
(即 class DerivedX : public virtual Base
),那么我认为您可以将其与多重继承只得到 'an extra method during testing' (我还没有测试过):
struct BaseExt : virtual Base {
void inject_data(int data) { _data = data; }
}
template<typename T>
struct TestDerived<T> : virtual T, virtual BaseExt {}
void doSomeTesting() {
std::shared_ptr<TestDerived<DerivedX>> p1 = std::make_shared<TestDerived<DerivedX>>();
std::shared_ptr<DerivedX> p2 = p1;
std::shared_ptr<BaseExt> p3 = p1;
assert(p1->data()==0);
assert(p2->data()==0);
assert(p3->data()==0);
p3->inject_data(10);
assert(p1->data()==10);
assert(p2->data()==10);
assert(p3->data()==10);
}