让我告诉你我的问题。我正在设计一组类来控制数字设备。该设备可以在两种操作模式下工作。在第一模式中,它可以执行一组特定的操作,而在第二模式中,它可以执行另一组操作(在两者之间可能有一些共同的操作)。我也可以在运行时更改设备的模式,因此如有必要,我可以在两种模式之间切换。与模式无关,器件使用相同的寄存器组。
我正在考虑用每个模式的一个基类来解决这个问题,所以当我需要第一组操作和模式2的对象时,当我需要第二组操作时,我可以拥有模式1的对象。然后我可以从这两个基类派生一个类,所以我可以拥有执行所有操作的对象。
我的设计的问题是两个基类有一些共同的功能和对相同寄存器的引用。由于我无法阻止成员的继承,因此我在派生类中会有重复项。我知道我可以选择使用范围运算符访问哪些副本,但我仍然认为这是一个糟糕的设计。
所以我的问题是:是否有一种解决这个问题的惯用方法?
如果没有正确或简单的方法来解决这个问题,我就会考虑设计3层次独立的类。我会有一些重复的代码,但这不是一个大问题,对吧?
以下代码(简化)用于说明:
class mode1
{
protected:
volatile uint8_t& reg1;
volatile uint8_t& reg2;
uint8_t data;
public:
virtual void operation1() final { // do something }
virtual void operation2() final { // do something }
virtual void operation3() final { // do something }
};
class mode2
{
protected:
volatile uint8_t& reg1;
volatile uint8_t& reg2;
uint8_t data;
public:
virtual void operation4() final { // do something }
virtual void operation2() final { // do something }
virtual void operation5() final { // do something }
};
class mode1and2 : public mode1, public mode2
{
public:
void operation6() { // do something }
void operation7() { // do something }
};
注意模式1和2具有operation2和所有数据成员的共同点。
答案 0 :(得分:1)
状态设计模式看起来很适合您的情况。
作为一个最小的工作示例:
#include<memory>
#include<iostream>
struct Behavior {
virtual void f() = 0;
virtual void g() = 0;
};
struct NullBehavior: Behavior {
void f() override {}
void g() override {}
};
struct Mode1: Behavior {
void f() override { std::cout << "mode 1 - f" << std::endl; }
void g() override { std::cout << "mode 1 - g" << std::endl; }
};
struct Mode2: Behavior {
void f() override { std::cout << "mode 2 - f" << std::endl; }
void g() override { std::cout << "mode 2 - g" << std::endl; }
};
struct Device {
template<typename B>
void set() { behavior = std::unique_ptr<Behavior>{new B}; }
void f() { behavior->f(); }
void g() { behavior->g(); }
private:
std::unique_ptr<Behavior> behavior{new NullBehavior};
};
int main() {
Device device;
device.f();
device.g();
device.set<Mode1>();
device.f();
device.g();
device.set<Mode2>();
device.f();
device.g();
}
从设备用户的角度来看,您所使用的模式并不重要。无论如何,根据要求,您可以随时动态更改它,您的设备将从此时开始使用新模式。
由于名称冲突,优先考虑组合而不是继承解决了这个问题。委托从外部类到内部状态的所有内容完成剩下的工作。
请注意,如果要在状态之间共享方法,则不会阻止您将它们放在基类中。
略有不同的版本可帮助您在两者之间共享数据:
struct Data {
volatile uint8_t& reg1;
volatile uint8_t& reg2;
uint8_t data;
};
struct Behavior {
virtual void f(Data &) = 0;
virtual void g(Data &) = 0;
};
struct NullBehavior: Behavior {
void f(Data &) override {}
void g(Data &) override {}
};
struct Mode1: Behavior {
void f(Data &) override { /* ... */ }
void g(Data &) override { /* ... */ }
};
struct Mode2: Behavior {
void f(Data &) override { /* ... */ }
void g(Data &) override { /* ... */ }
};
struct Device {
template<typename B>
void set() { behavior = std::unique_ptr<Behavior>{new B}; }
void f() { behavior->f(data); }
void g() { behavior->g(data); }
private:
Data data{};
std::unique_ptr<Behavior> behavior{new NullBehavior};
};
对于特定模式而言唯一的所有参数可以是类定义的一部分,也可以放在Data
中,如果您使用的是其他模式,则会被忽略。
答案 1 :(得分:0)
我将mode1
和mode2
的公共部分放在一个公共基类中,让我们说Common
,然后包含您的数据和成员函数{ {1}}。然后,与虚拟继承一起,您可以在同一数据上拥有两个视图,即使在需要时也可以同时使用。
operation2