C ++重构怪物类的帮助

我有一个C背景,在C ++上是一个newb。 我有一个基本的设计问题。 我有一个班级(我会称之为“厨师”B / C我看起来非常类似于这个问题,无论是在复杂性和问题方面)基本上像这样工作

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

    private: 
          char name;
          char dish_responsible_for;
          int shift_working;
          etc...
    }

在伪代码中,这是按照以下方式实现的:

   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();
         }
   }

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

  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...
  }

     class plater : kitchen_worker
     {
     public:
         void plate();
     }

等等...

我承认仍然在如何在运行时间实施它,所以如果例如电镀(或“以电镀的厨师”的身份)决定通过晚餐服务中途回家,那么厨师必须工作一个新的班次。

这似乎与我有一个更广泛的问题有关,如果同一个人在这个例子中总是做准备,烹饪和电镀,那么让这个阶层的层次结构模拟单个厨师做什么,真正的实际优势是什么? 我认为这会导致“担心增加班级”的事情,但同时,现在或者在可预见的将来,我认为维持厨师班的整体工作是非常麻烦的。 我还认为,对于一个天真的代码阅读者来说,在厨师对象中看到三种不同的方法并继续前进,这对于真正意义上的事情来说更加容易。

我知道当我们添加诸如“cut_onions()”,“cut_carrots()”等方法时,它可能会变得很笨拙,可能每个方法都有自己的数据,但似乎这些可以通过让prep()函数,比如说更模块化。 此外,似乎SRP采用其合乎逻辑的结论会创建一个“onion_cutters”“carrot_cutters”等类别......我仍然很难看到它的价值,因为程序必须确保同一位员工切割洋葱和胡萝卜,这有助于保持各种方法的状态变化相同(例如,如果员工切割洋葱的手指,他不再有资格切胡萝卜),而在怪物对象厨师类中,似乎所有这些都得到了照顾。

当然,我明白,这并不意味着有一个有意义的“面向对象设计”,但在我看来,如果我们必须为每个厨师的任务分别设置不同的对象(这看起来不自然,因为同一个人是完成这三项功能),那么似乎优先于概念模型的软件设计。 如果我们想拥有可能是不同的人的“meat_chef”“sous_chef”“three_star_chef”,我觉得面向对象的设计在这里很有帮助。 此外,与运行时问题有关的是,在单一责任原则的严格应用下,似乎存在复杂性的开销,必须确保构成基类员工的基础数据得到改变,并且这种改变是反映在随后的时间步骤中。

因此,我很愿意按照原样离开它。 如果有人能够澄清为什么这是一个坏主意(如果你有关于如何最好地继续进行的建议),我会非常感激。


为了避免现在和将来的滥用类heirarchies,你应该只用它当关系存在。 作为你自己,“厨师是厨师”。 它在现实生活中显然没有意义,也不在代码中。 “cook_food”是一个动作,所以创建一个动作类和子类可能是有意义的。

有一个新类只是为了添加像cook()prep()这样的新方法,对于原始问题并不是真正的改进 - 因为你所做的所有事情都是将方法包装在类中。 你真正想要做的是做一个抽象来做这些动作 - 所以回到动作类。

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

class cook_food : public action {
    public:
        virtual void perform_action() {
            //do cooking;
        }
}

然后可以给厨师一个按您指定的顺序执行的操作列表。 比如说,一个队列。

class chef {
    ...
        perform_actions(queue<action>& actions) {
            for (action &a : actions) {
                a.perform_action();
            }
        }
    ...
}

这通常被称为战略模式。 它通过允许您在不修改现有类的情况下添加新操作来促进开放/关闭原则。


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

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() { ... }
}

另一个可能适合于此的密切相关的模式是Builder模式

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


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

一步就是创建一个抽象来说一些东西是可裁剪的。 对于子类就是关系是候选人,因为胡萝卜是可切割的。

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

class carrot : public cuttable {
    public:
      virtual void cut() {
          //specific code for cutting a carrot;
      }
}

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

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
        }
}

链接地址: http://www.djcxy.com/p/11377.html

上一篇: C++ Help on refactoring a monster class

下一篇: gradients artifacts in Mozilla Firefox