原型设计模式例子 (c++)

3

我正在学习原型设计模式,并对 sourcemaking 上的 this article 中给出的示例感到困惑。

class Stooge
{
public:
   virtual void slap_stick() = 0;
   virtual Stooge* clone() = 0;
};

class Larry : public Stooge
{
public:
   void slap_stick()
   {
      cout << "Larry: poke eyes\n";
   }
   Stooge*   clone() { return new Larry; }   
};
class Moe : public Stooge
{
public:
   void slap_stick()
   {
      cout << "Moe: slap head\n";
   }
   Stooge*   clone() { return new Moe; }   
};
class Curly : public Stooge
{
public:
   void slap_stick()
   {
      cout << "Curly: suffer abuse\n";
   }
   Stooge*   clone() { return new Curly; }
};

class Factory
{
public:
   static Stooge* make_stooge( int choice );
private:
   static Stooge* s_prototypes[4];
};

Stooge* Factory::s_prototypes[] = {0, new Larry, new Moe, new Curly};

Stooge* Factory::make_stooge( int choice )
{
   return s_prototypes[choice]->clone();
}

int main()
{
   vector roles;
   int    choice;

   while (true)
   {
      cout << "Larry(1) Moe(2) Curly(3) Go(0): ";
      cin >> choice;
      if (choice == 0)
         break;
      roles.push_back(Factory::make_stooge( choice ) );
   }

   for (int i=0; i < roles.size(); ++i)
      roles[i]->slap_stick();
   for (int i=0; i < roles.size(); ++i)
      delete roles[i];
}

根据原型设计模式的描述:
  • 使用原型实例指定要创建的对象类型,并通过复制该原型创建新对象。
根据this。原型设计模式是一种通过复制或克隆现有对象的属性来实例化类的设计模式。
据我所知,复制类的常规方式是使用复制构造函数、重载运算符=或实现克隆函数来通过复制现有对象的所有属性来实例化一个新对象。
在上面的例子中,我没有看到它如何通过复制原型来创建新对象,既没有定义复制构造函数,也没有重载运算符=或适当的克隆函数。
那么我可以认为这不是原型设计模式的实现吗?还是我对我的假设理解错误? 编辑:如@songyuanyao所提到的

在这个例子中,它是新创建的对象,没有复制任何东西

所以我认为上述例子不是原型模式的合适示例,因为它并没有代表原型模式的主要目标。


1
在这个例子中,它是新创建对象,而不是复制任何东西。 - songyuanyao
没错,正如您所说,“它在创建新的对象时,不复制任何东西”,因此它并未代表原型设计模式的主要思想。 - T M
使用复制构造函数可能更有意义。例如 return new Larry(*this); - songyuanyao
1个回答

3

首先,在调用对象的方法进行克隆不是必要的,完全有可能你拥有一个ManagerFactory,其具有用于克隆对象的方法,例如:

Object Factory::clone( Object &){

}

此外,虽然您可以使用复制构造函数,但复制构造函数仅适用于具体类。如果出于良好的设计决策,您只向用户提供对象 API(接口/纯虚函数),则无法在客户端使用复制构造函数。因此,如果打算使用克隆项,则可以通过添加Clone方法来解决该问题。

class BaseVirtualClass{
public:
    virtual int foo() = 0;
    virtual BaseVirtualClass * clone() = 0;
    virtual ~BaseVirtualClass(){}
};

class DerivedClass: public BaseVirtualClass{
    int state;
public:
    DerivedClass(int a):state(a){}
    DerivedClass( const DerivedClass & other)
        : state(other.state){}
    int foo(){ // override
        return state;
    }

    BaseVirtualClass * clone(){ //override
         // I'm using copy constructor here, but hidden from user
         return new DerivedClass( *this);
    }
};

使用方法:

BaseVirtualClass * obj = factory.createObject();
BaseVirtualClass * clone = obj->clone();

有时候,您可能需要通过工厂进行操作:
BaseVirtualClass * obj = factory.createObject();
BaseVirtualClass * clone = factory.clone(obj);

注意:除了修复当您提供指向抽象类的指针时缺少副本构造函数外,Clone方法具有明确的意图,即创建一个深层复制以复制对象状态,而无需编写样板代码来重新创建该特定状态的对象。当创建对象的责任不属于对象时(因为您必须使用具有自定义分配器或其他复杂依赖项的工厂)则将克隆方法移动到工厂中。


编辑:

问题提出者在互联网上找到的代码示例对我来说似乎是一个边缘案例。 这些对象在技术上是克隆的(除类型外,它们没有状态,因此它们具有相同的状态),但我认为这不是原型模式的好例子:

  1. 当有需要以某种方式复制有趣状态的复杂对象时,我想使用原型模式。
  2. 最重要的部分是状态的复制,因此内部实现也应该明确说明(将具有相同状态的对象留下可能会对内部文档目的不清晰)

1
是的,感谢您的解释。由于原型设计模式的主要目标是通过克隆现有实例(使用复制构造函数或任何其他方法)来创建新实例,并且我发布的示例并没有这样做,而只是创建了新对象,因此我认为它不是原型模式的适当示例,因为它不代表原型模式的主要目标。 - T M
我明白了,你在网上找到的代码不是一个好的学习例子,但从技术上讲,它并没有违反“克隆契约”:实际上它创建了一个新对象,该对象仍然是一个克隆(该对象没有状态,除了类型以外,因此创建一个新对象会产生另一个具有相同状态的对象,可以将其视为一种糟糕的实现)。当然,这是一个可能永远不会在实际应用中发生的边角情况。如果我创建的代码示例对你足够了,你能接受它吗?谢谢 :) - CoffeDeveloper
1
是的,这个评论除了你的答案似乎也在回答我的问题。所以也许你可以将这个评论添加到你的答案中,我会接受 :) - T M
好的,我已经将它添加到答案中 :) - CoffeDeveloper
请阅读我的下面的评论,如果您知道这个模式的完整细节,请回复。 - ssg
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接