【问题标题】:Decision making, complex conditions and planning easy maintenance决策、复杂条件和规划 易于维护
【发布时间】:2014-11-23 15:40:31
【问题描述】:

我正在尝试找到一种优雅的方式来实现一种易于维护的决策算法,因为决策条件可能经常变化。

我将在这里尝试更具体的示例:

假设我正在尝试管理餐厅厨房的烹饪厨师团队。

每个厨师都知道如何烹制 3 种派:苹果派、南瓜派和覆盆子派,以及 2 种比萨:奶酪比萨和培根比萨。他们都知道如何烹饪所有东西。

现在,我想向这些主管发送订单,告知客户即将发生的事情。

条件是:

酋长一次只能做一个馅饼。例如,如果我命令厨师做苹果派,我不能命令他做覆盆子派或南瓜派,除非苹果派做好或我发送了取消苹果派的请求。

考虑到不同的客户,我可以让厨师一次最多做 5 个比萨饼。

我想创建一个算法,返回允许我发送给特定厨师的一组订单,关于他已经在烹饪的东西。

我正在使用 C++。 我可以写一个简单的 switch/case 语句,但是如果条件改变或添加新的馅饼,维护起来就不容易了,所以......

我有点卡住了,真的不知道如何封装条件和决策,以减少条件之间的耦合,并在馅饼烹饪条件下轻松维护。

您将如何处理复杂的决策算法实现?

【问题讨论】:

  • 如果您正在寻找它的一般设计决策,programmers.se 可能更适合这个问题。
  • 那我会转移我的问题,谢谢

标签: c++ maintenance design-decisions


【解决方案1】:

我可以写一个简单的 switch/case 语句,但是如果条件改变或添加新的馅饼,维护会很困难,所以......

我有点卡住了,真的不知道如何封装条件和决策以减少条件之间的耦合,并允许在馅饼烹饪条件下轻松维护。

您将如何处理复杂的决策算法实现?

从 switch/case 到可维护 OOP 的经典更改/重构是将每个条件和操作替换为抽象类专业化/实现。

旧代码:

variable_t variable; // initialized elsewhere
switch(variable) {
case value1:
    do_action1();
    break;
case value2:
    do_action2();
    break;
// ...
}

新代码:

struct Actor // actor is your "abstract chef"
{
    virtual ~Actor();
    virtual bool matches(variable_t const v) const = 0;
    virtual void do_action() = 0;
};

现在,对于每个动作和条件组合,您创建一个专业化:

struct SweedishChef: public Actor {
    bool matches(variable_t const v) const override
    {
         return v == 1;
    }

    void do_action() override
    {
         std::cerr << "bork! bork!\n";
    }
};

有了这个,客户端代码不再有任何硬编码。

客户端代码的初始化:

std::vector<std::unique_ptr<Actor>> actors;
actors.emplace_back( new SweedishChef{} };
// adding a new type of chef simply means adding _one_ line of
// code here, for the new type

决策代码(替换旧的switch代码):

// using std::find, begin, end
variable_t variable; // initialized elsewhere
const auto chef = find(begin(actors), end(actors),
    [&v](const std::unique_ptr<Actor>& a) { return a->matches(v); });

if(chef != end(actors))
    chef->do_action();
else
{
    // here goes whatever was in the default part of the switch code
}

从维护和可测试性的角度来看,这段代码更易于维护:

  • 添加新厨师时,客户端代码的更改很少

  • 厨师和命令之间的交互已在界面后面形式化(并冻结)

  • 每个条件/动作都可以(并且应该)单独测试

  • 可以单独测试调度机制(并使用模拟注入的actors,以应对各种情况)。

  • 初始化机制可以单独测试

【讨论】:

  • 非常准确,谢谢。这类似于策略模式吗?
【解决方案2】:

没有。编码太多了。只计算符号。这种编码不能保证为“new SwedishChef{}”释放清晰的内存。并且客户端大小在变量声明中变得更长。

【讨论】:

  • @user4090727,该问题专门要求一种减少耦合并使代码更加模块化/可维护的设计(而不是符号较少的代码)。此外,“这种类型的条件”称为 RAII(即使用 new 分配并将结果放在 std::unique_ptr 中的 std::vector 中)并且它确实实际上保证了内存释放“新瑞典厨师{}”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
相关资源
最近更新 更多