为什么他们在原型模式中这么说 - 用于在需要新对象时简单地复制原始对象



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

但是,看着维基百科的文章 - 我无法理解以下几点。

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

  2. 避免以标准方式创建新对象的固有成本(例如,使用"new"关键字),因为对于给定的应用程序来说,新对象非常昂贵。

这是我创建的程序,用于演示C++中的原型模式。但是,我无法从中找到任何好处。为什么原型模式将有助于在此处快速创建对象。我可以看到对象每次都必须调用"new"。这是整个程序,如果您认为我没有正确实现原型模式,请纠正我。

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

像工厂对象一样 - 这是原型类

 -- 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 数据包的具体类型 -

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

我在这里找不到任何与"新"相关的好处。请对此进行一些说明。

我可以尝试解释第一点:

而不是每次新的数据

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

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

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"关键字)当它过于昂贵时 给定的应用程序。

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

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

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

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

事实上,您无法从原型模式中找到任何好处,这可能意味着它不适合您的情况,并且最好使用不同的模式,例如对象池,蝇量级或抽象工厂模式。

最新更新