原型设计模式的意义何在?

所以我在学校里学习设计模式,今天我被告知了“原型”设计模式。

我肯定漏掉了什么,因为我看不到它的好处。我在网上看到有人说它比使用 new快,但是这没有意义; 在某些时候,不管新对象是如何创建的,都需要为它分配内存。

这种模式不是和“先有鸡还是先有蛋”的问题一样吗?由于 Prototype 模式本质上只是克隆对象,因此在某个时候必须自己创建原始对象(即不克隆)。这将意味着我需要有一个现有的副本,每个对象,我想克隆已经准备好克隆?

有人能解释一下这个图案的用途吗?

26423 次浏览

Prototype 模式是基于克隆预配置对象的创建模式。这个想法是,您选择一个对象,这个对象被配置为默认的或者在某个特定用例的大致范围内,然后您克隆这个对象并配置到您确切的需求。

当所需的配置非常繁琐时,该模式对于删除大量样板代码非常有用。我认为 Prototype 是一个预先设置好的对象,您可以在其中保存一组状态作为一个新的起点。

原型模式有一些好处,例如:

  • 它消除了初始化对象的开销(潜在的开销)
  • 它简化并优化了同一类型的多个对象拥有大部分相同数据的用例

例如,假设您的程序使用的对象是根据从网络上检索的大多数不变信息解析出的数据创建的。与其在每次创建新对象时检索数据并重新解析它,不如在需要新对象时使用原型模式来简单地复制原始对象。

此外,假设该对象可能拥有占用大量内存的数据,例如表示图像的数据。可以通过使用写上复制样式的继承来减少内存,其中显示原始的、未重复的数据,直到代码尝试更改该数据。然后,新数据将屏蔽以引用原始数据。

如果您有一个需求,您需要填充或使用包含 Object 可重复的相同数据

还有

不可能从现有对象生成,例如[使用网络流生成对象] 或者

建立一个对象是很耗时的[建立一个大对象,从数据库中获取数据]然后使用这个设计模式,因为在这个拷贝中,现有对象被创建,这个拷贝将不同于原始对象,可以像原始对象一样使用。

如果您想要创建一个对象,但是不想经历网络或数据库调用所在的昂贵的对象创建过程,那么使用原型模式。只需创建对象的一个副本并对其进行更改。

与抽象工厂模式相比,通过使用原型模式,您不需要有一个很大的工厂层次结构,只需要一个很大的产品层次结构。

这里的许多其他答案都谈到了克隆一个已经配置好的对象可以节省成本,但是我想扩展一下 Prototype 模式的另一个“要点”。在某些语言中,类被视为第一类对象,您可以通过简单地传递类名来配置客户机在运行时创建的对象类型。在像 C + + 这样的语言中,类不被视为一级对象,Prototype 模式允许您实现同样的效果。

例如,让我们说我们有一个 Chef在一家餐厅的工作是制作和服务的饭菜。我们假设 Chef的工资太低,而且心怀不满,所以他做的菜如下:

class Chef {
public:
void prepareMeal() const {
MozzarellaSticksWithKetchup* appetizer = new MozzarellaSticksWithKetchup();
// do something with appetizer...


HockeyPuckHamburgerWithSoggyFries* entree = new HockeyPuckHamburgerWithSoggyFries();
// do something with entree...


FreezerBurnedIceCream* dessert = new FreezerBurnedIceCream();
// do something with dessert...
}
};

现在,让我们说,我们想要改变 Chef成为一个招摇的名厨。这意味着他/她必须在 prepareMeal()new不同的菜。我们希望修改这个方法,以便通过 Chef获得 new的膳食类型可以指定为参数。在类是第一类对象的其他语言中,我们可以简单地将类名作为参数传递给方法。我们不能在 C + + 中这样做,所以我们可以从原型模式中受益:

class Appetizer {
public:
virtual Appetizer* clone() const = 0;
// ...
};


class Entree {
public:
virtual Entree* clone() const = 0;
// ...
};


class Dessert {
public:
virtual Dessert* clone() const = 0;
// ...
};


class MozzarellaSticksWithKetchup : public Appetizer {
public:
virtual Appetizer* clone() const override { return new MozzarellaSticksWithKetchup(*this); }
// ...
};


class HockeyPuckHamburgerWithSoggyFries : public Entree {
public:
virtual Entree * clone() const override { return new HockeyPuckHamburgerWithSoggyFries(*this); }
// ...
};


class FreezerBurnedIceCream : public Dessert {
public:
virtual Dessert * clone() const override { return new FreezerBurnedIceCream(*this); }
// ...
};


// ...and so on for any other derived Appetizers, Entrees, and Desserts.


class Chef {
public:
void prepareMeal(Appetizer* appetizer_prototype, Entree* entree_prototype, Dessert* dessert_prototype) const {
Appetizer* appetizer = appetizer_prototype->clone();
// do something with appetizer...


Entree* entree = entree_prototype->clone();
// do something with entree...


Dessert* dessert = dessert_prototype->clone();
// do something with dessert...
}
};

注意,clone()方法创建派生类型的实例,但返回指向父类型的指针。这意味着我们可以更改通过使用不同的派生类型创建的对象的类型,而客户端不会知道其中的差异。这种设计现在允许我们配置一个 Chef——我们的 Prototype 的客户端——来在运行时制作不同类型的碟形天线:

Chef chef;


// The same underpaid chef from before:
MozzarellaSticksWithKetchup mozzarella_sticks;
HockeyPuckHamburgerWithSoggyFries hamburger;
FreezerBurnedIceCream ice_cream;
chef.prepareMeal(&mozzarella_sticks, &hamburger, &ice_cream);


// An ostentatious celebrity chef:
IranianBelugaCaviar caviar;
LobsterFrittataWithFarmFreshChives lobster;
GoldDustedChocolateCupcake cupcake;
chef.prepareMeal(&caviar, &lobster, &cupcake);

您可能想知道,用这种方法,Prototype 模式可以为您购买与 Factory Method 模式相同的东西,那么为什么不直接使用它呢?因为 Factory Method 模式需要一个创建者类的层次结构,它反映了正在创建的产品的层次结构; 也就是说,我们需要一个带有 make()方法的 MozzarellaSticksWithKetchupCreator,一个带有 make()方法的 HockeyPuckHamburgerWithSoggyFriesCreator,等等。因此,您可以简单地将 Prototype 模式视为减轻由 Factory Method 模式引入的代码冗余的一种方法。

这个论点来自 设计模式: 可重用面向对象软件的元素,也就是“四人帮”的书。

使用原型模式完全取决于您的问题。在大多数情况下,克隆和创建新对象之间没有任何区别。 但是 如果你在构造函数中做一些复杂的或者耗时的操作,或者在设置一个属性的时候,它必须做一些复杂的和耗时的操作,原型模式将会帮助我们。因为从旧实例复制对象到新实例更容易,性能更高。(深度克隆)。因此,这种模式与对象更加兼容,因为它们的状态不会长时间改变。 在使用此模式之前完全分析您的问题。