C++帮助重构怪物类

use*_*399 7 c++ oop refactoring anti-patterns

我有一个C背景,并且是C++的新手.我有一个基本的设计问题.我有一个班级(我称之为"厨师"b/c我的问题似乎与此类似,无论是在复杂性还是问题方面)基本上都是这样的

    class chef
    {
    public:
          void prep();
          void cook();
          void plate();

    private: 
          char name;
          char dish_responsible_for;
          int shift_working;
          etc...
    }
Run Code Online (Sandbox Code Playgroud)

在伪代码中,这可以通过以下方式实现:

   int main{
    chef my_chef;
    kitchen_class kitchen;
    for (day=0; day < 365; day++)
         {
         kitchen.opens();
         ....

         my_chef.prep();
         my_chef.cook();
         my_chef.plate();

         ....

         kitchen.closes();
         }
   }
Run Code Online (Sandbox Code Playgroud)

这里的厨师班似乎是一个怪物类,并有可能成为一个.厨师似乎也违反了单一的责任原则,所以我们应该有这样的事情:

  class employee
  {
  protected: 
        char name;
        int shift_working;
  }

  class kitchen_worker : employee
  {
  protected: 
        dish_responsible_for;
  }

  class cook_food : kitchen_worker
  {
  public:
        void cook();
        etc...
  }
  class prep_food : kitchen_worker
  {
  public:
        void prep();
        etc...
  }
Run Code Online (Sandbox Code Playgroud)

     class plater : kitchen_worker
     {
     public:
         void plate();
     }
Run Code Online (Sandbox Code Playgroud)

等等...

我承认自己仍然在努力如何在运行时实施它,所以,如果例如电镀(或"厨师作为电镀者")决定在晚餐服务中途回家,那么厨师必须进行新的班次.

这似乎与我所提出的更广泛的问题有关,如果同一个人总是在这个例子中做准备,烹饪和电镀,那么让这个等级的类来模拟单个厨师做什么的真正的实际优势是什么?我想,运行到的东西"并称类的恐惧",但在同一时间,现在还是在可预见的未来,我不认为在保持整体厨师类是非常麻烦的.我也认为这是一个非常现实的意义更容易为代码的天真读者看到厨师对象的三种不同的方法,继续前进.

我明白,如果我们添加像"cut_onions()","cut_carrots()"等方法,或者每个方法都有自己的数据,它可能会变得笨拙,但看起来这些可以通过制作来处理prep()函数,比如说更模块化.此外,似乎SRP得出其合乎逻辑的结论将创建一个类"洋葱切割器""胡萝卜切割器"等...我仍然很难看到它的价值,因为不知何故该程序必须确保同一个员工切洋葱和胡萝卜,这有助于保持状态变量相同的方法(例如,如果员工切割他的手指切洋葱他不再有资格切胡萝卜),而在怪物对象厨师类似乎所有这些都得到了照顾.

当然,我知道这对于有意义的"面向对象设计"变得不那么重要了,但在我看来,如果我们必须为每个厨师的任务分别设置对象(这似乎不自然,因为同一个人是完成所有三个功能)然后,这似乎优先于概念模型的软件设计.我觉得一个面向对象的设计是在这里有益的,如果我们希望有,比方说,"meat_chef""sous_chef""three_star_chef"有可能不同的人.此外,相关的运行问题是,在复杂的开销似乎的单一职责原则,是要确保以建立基础类员工基础数据得到改变严格的应用下,并且这种变化是反映在随后的时间步骤中.

因此,我很想把它或多或少地留下来.如果有人可以解释,为什么这将是一个糟糕的主意(如果你有建议如何以最佳方式进行),我不胜感激.

Mar*_*k H 3

为了避免现在和将来滥用类层次结构,您实际上应该只在存在is关系时才使用它。作为你自己,“cook_food 是一名 kitchen_worker”。显然,它在现实生活中没有意义,在代码中也没有意义。“cook_food”是一个操作,因此创建一个操作类并对其进行子类化可能是有意义的。

拥有一个新类只是为了添加新方法,例如cook()和 ,prep()无论如何,这并不是对原始问题的真正改进 - 因为您所做的只是将方法包装在类中。您真正想要的是进行抽象来执行这些操作中的任何一个 - 所以回到操作类。

class action {
    public:
        virtual void perform_action()=0;
}

class cook_food : public action {
    public:
        virtual void perform_action() {
            //do cooking;
        }
}
Run Code Online (Sandbox Code Playgroud)

然后,可以向厨师提供一系列操作,以按照您指定的顺序执行操作。举个例子,一个队列。

class chef {
    ...
        perform_actions(queue<action>& actions) {
            for (action &a : actions) {
                a.perform_action();
            }
        }
    ...
}
Run Code Online (Sandbox Code Playgroud)

这通常称为策略模式。它允许您在不修改现有类的情况下添加新操作,从而促进开放/封闭原则。


您可以使用的另一种方法是模板方法,您可以在其中指定一系列抽象步骤,并使用子类来实现每个步骤的特定行为。

class dish_maker {
    protected:
        virtual void prep() = 0;
        virtual void cook() = 0;
        virtual void plate() = 0;

    public:
        void make_dish() {
            prep();
            cook();
            plate();
        }
}

class onion_soup_dish_maker : public dish_maker {
    protected:
        virtual void prep() { ... }
        virtual void cook() { ... }
        virtual void plate() { ... }
}
Run Code Online (Sandbox Code Playgroud)

另一个可能适合于此的紧密相关的模式是构建器模式

这些模式还可以减少顺序耦合反模式,因为很容易忘记调用某些方法,或以正确的顺序调用它们,特别是在多次执行时。您还可以考虑将 kitchen.opens() 和 closes() 放入类似的模板方法中,这样您就不必担心 closes() 被调用。


在为onion_cutter和carrot_cutter创建单独的类时,这实际上并不是SRP的逻辑结论,但实际上违反了它——因为你正在创建负责切割的类,并保存一些关于它们是什么的信息切割。切割洋葱和胡萝卜都可以抽象为单个切割操作 - 您可以指定要切割的对象,如果您需要每个对象的特定代码,则可以向每个单独的类添加重定向。

第一步是创建一个抽象来表示某些东西是可切割的。子类化的is关系候选关系,因为胡萝卜可切的。

class cuttable {
    public:
        virtual void cut()=0;
}

class carrot : public cuttable {
    public:
      virtual void cut() {
          //specific code for cutting a carrot;
      }
}
Run Code Online (Sandbox Code Playgroud)

剪切动作可以采用可剪切对象并执行适用于所有可剪切对象的任何常见剪切动作,并且还可以应用每个对象的特定剪切行为。

class cutting_action : public action {
    private:
        cuttable* object;
    public:
        cutting_action(cuttable* obj) : object(obj) { }
        virtual void perform_action() {
            //common cutting code
            object->cut(); //specific cutting code
        }
}
Run Code Online (Sandbox Code Playgroud)