首页 > 解决方案 > Correct use of friend for using multiple strategies without losing state?

问题描述

I have a class with some data, which I want to represent in different ways. There will always only be one representation active at a time, but I need to be able to change between them at runtime without losing internal state of any of the representations.

As a small example, let us use a chart. It can either be a pie-chart or a bar-chart, nothing else and only one at a time. I want to use the same data for both charts, but each of them represents the data differently. So both types of charts need their own rules for how to treat the data, which can change at runtime by the user.

I do not want to deal with each case in the chart class, but rather use something that handles every type of chart and calls the correct functions depending on the active one. All used variants of charts are known at compile time (two in this example).
A simple inheritance does not work, because the data is not shared between the charts. The strategy-pattern also does not quite work, because the state is lost upon switching charts and I need to preserve it. For the same reason std::variant does not work in this case.

My current solution is similar to the strategy-pattern, but keeping each strategy alive in a manager class. Each strategy has a pointer to the chart class and is a friend of it to access the data:

struct Chart;

struct Strat {
    explicit Strat(Chart* chart) : chart {chart} {}
    virtual void foo() = 0;
    Chart* chart;
};

struct Pie : public Strat {
    explicit Pie(Chart* chart) : Strat {chart} {}
    void foo() override { /* use chart->data */ }
};

struct Bar : public Strat {
    explicit Bar(Chart* chart) : Strat {chart} {}
    void foo() override { /* use chart->data */ }
};

struct Manager {
    explicit Manager(Chart* chart) : pie {chart}, bar {chart} { strat = &pie; }
    void changeToBar() { strat = &bar; }
    void foo() { strat->foo(); }
    Strat* strat;
    Pie pie;
    Bar bar;
};

struct Chart {
    Chart() : manager {this} { manager.foo(); }
    void changeToBar() { manager.changeToBar(); }
    void foo() { manager.foo(); }
    friend Pie;             // friends to make data accessible
    friend Bar;
private:
    Manager manager;
    int data = 42;          // private data, shared by all strats
};

int main() {
    Chart chart;            // inititally pie chart
    chart.foo();            // do pie stuff
    chart.changeToBar();    // now bar chart, but keep pie alive
    chart.foo();            // do bar stuff
}

To me it feels like there is a better solution to do the same thing, but I could not find it, so my question is: is this the correct way of dealing with multiple strategies, while preserving the state?

标签: c++friendstrategy-pattern

解决方案


我想您已经想到了这一点,但是您可以创建一个指向您的数据结构的共享指针,并将其传递给您的所有图表结构。任何新的图表类型都将很容易添加,只需传递相同的指针。如果您在图表构造函数中传递数据结构指针,那么当您的客户在路上请求新的图表类型时,它也可以作为自我文档。


推荐阅读