【问题标题】:Why do they say that in the Proto-type Pattern - be used to simply duplicate the original object whenever a new one is needed?为什么他们在原型模式中这么说——在需要新对象时简单地复制原始对象?
【发布时间】:2015-05-17 05:50:49
【问题描述】:

我正在努力学习设计模式。我是一名 C++ 程序员。目前,我正在处理原型模式。我可以将原型与工厂类型关联起来。但是,工厂模式和原型模式之间存在很多差异。例如,在原型模式中,每个派生类都向基类/超类注册其原型。

但是,看着wikipedia article - 我无法理解以下几点。

  1. 无需在每次创建新对象时检索数据并重新解析它,原型模式可用于在需要新对象时简单地复制原始对象。

  2. 避免以标准方式(例如,使用“new”关键字)创建新对象的内在成本,因为它对于给定的应用程序来说过于昂贵。

这是我为演示 C++ 中的原型模式而创建的程序。但是,我找不到任何好处。原型模式如何有助于在这里快速创建对象。我可以看到该对象每次都必须调用“新”。这是整个程序,如果您认为我没有正确实现原型模式,请纠正我。

抱歉,程序太长了 - 但相信我,这很简单。

就像一个工厂对象——这里是原型类

 -- basically an abstract.

class Itransport
{

public:

enum transportPacketType
 {
   udp,
   tcp,
   MAX
 };

private:
     static std::list<Itransport *> prototypesList;

protected:
     virtual Itransport::transportPacketType getPacketType() = 0;
     virtual Itransport* clone() = 0;

     /** This will be called by the derived classes **/
     static void  registertoPrototypeList(Itransport *packet)
     {
        prototypesList.push_back(packet);
     }
public:

     virtual void showMessage() = 0;

 static Itransport* makeClone(Itransport::transportPacketType packType)
     {
        std::list<Itransport *>::iterator it;

        for(it = prototypesList.begin(); it != prototypesList.end(); it++)
           {
              if( (*it)->getPacketType() == packType )
                 {
                    return (*it)->clone();
                 }
           }
     }

     virtual ~Itransport() = 0;
};

Itransport::~Itransport()
{
   std::cout<<"Itransport Destructor called"<<std::endl;
}

std::list<Itransport *> Itransport::prototypesList;

这里是Itransport Packet的具体类型——

class udpPacket: public Itransport
{

private:

  static udpPacket udpTransportPacket;

protected:
Itransport::transportPacketType getPacketType()
{
   return  Itransport::udp;
}

Itransport* clone()
      {
           return new udpPacket();
      }

public:

void showMessage()
{
   std::cout<<"This is a UDP Packet"<<std::endl;
}

udpPacket()
{
   std::cout<<"UDP Packet Constructed"<<std::endl;
   registertoPrototypeList(this);
}

~udpPacket()
{
   std::cout<<"Destructor of udp called"<<std::endl;
}

};

static udpPacket udpTransportPacket;

这里是客户端 -

int main()
{
   Itransport *udpPacket;
   Itransport *udpPacket2;
   udpPacket = Itransport::makeClone(Itransport::udp);
   udpPacket->showMessage();

   udpPacket2 = Itransport::makeClone(Itransport::udp);
   udpPacket2->showMessage();

   delete udpPacket;
   delete udpPacket2;

   return 0;
}

我在这里找不到与“新”相关的任何好处。请给它一些亮点。

【问题讨论】:

  • 应该是 return new udpPacket(*this); 而不是 return new udpPacket();,因为您想创建原型的副本。

标签: c++ design-patterns


【解决方案1】:

我可以先解释一下第一点:

而不是每次获取新数据并重新解析它 对象被创建,原型模式可以用来简单地 每当需要新对象时复制原始对象。

想象一个必须创造大量怪物的电脑游戏。假设所有不同类型的怪物在编译时都不知道,但是您从一些输入数据构建了一个特定类型的怪物,这些数据提供了有关怪物颜色等信息:

class Monster {
 public:
  Monster(InputDataHandle handle) {
    // Retrieve input data...
    // Parse input data...
  }
  void setPosition(Position);
};

然后每次你要构造,说一个红色的怪物你必须检索数据并重新解析:

// Spawn a lot of red monsters
for (int i = 0; i != large_number; ++i) {
    auto red = new Monster(red_monster_data); // Must retrieve data and re-parse!
    red->setPosition(getRandomPosition());
    game.add(red);
} 

显然这是低效的。解决它的一种方法是使用原型模式。您创建了一个“原型”红色怪物,并且每次想要创建红色怪物的实例时,您只需复制原型,而无需检索和重新解析输入数据:

auto prototype_red_monster = new Monster(red_monster_data);

for (int i = 0; i != large_number; ++i) {
    auto red = prototype_red_monster->clone();
    red->setPosition(getRandomPosition());
    game.add(red);
}

但是克隆功能是如何实现的呢?这就引出了我不太明白的第二点:

避免以标准方式创建新对象的固有成本 (例如,使用“新”关键字)当它过于昂贵时 给定的应用程序。

克隆函数基本上必须为新对象分配内存并从自身复制数据。当他们谈论“new 关键字的固有成本”时,我不确定我是否知道他们指的是什么。这些示例在 Java 和 C# 中,分别具有 clone()MemberwiseClone()。在这些语言中,您无需致电new。我不知道clone()MemberwiseClone() 是如何实现的,但我看不出它们如何“避免new 关键字的固有成本”。

在 C++ 中,我们必须自己实现 clone(),它通常会使用 new 并使用复制构造函数:

Monster* clone() {
  return new Monster(*this);
}

在这种情况下,复制构造函数比从头创建对象要便宜得多。在你的情况下,它可能不是。

在您的案例中,您无法从原型模式中找到任何好处,这可能意味着它不适合您的案例,您最好使用不同的模式,例如 Object PoolFlyweightAbstract Factory图案。

【讨论】:

  • 我喜欢你的回答,但它清楚地告诉我,原型模式在 C++ 中没有任何好处。 Java 中的 clone() 与 new 操作符是不同的。
  • 我不同意 C++ 中原型模式没有任何好处。显然,我提供的示例有好处。还有许多其他有用的情况。它可能对您的情况没有用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-18
  • 1970-01-01
  • 1970-01-01
  • 2018-07-29
  • 2015-02-28
相关资源
最近更新 更多