这里的许多其他答案都谈到了克隆已配置对象的成本节省,但我想扩展原型模式的另一个“点”。在某些语言中,类被视为一等对象,您可以配置客户端在运行时创建的对象类型,只需向其传递类名即可。在像 C++ 这样的语言中,类不被视为一等对象,原型模式允许您实现相同的效果。
例如,假设我们在一家餐厅有一个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——我们原型的客户端——在运行时制作不同类型的菜肴:
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);
您可能想知道,使用这种方式,原型模式为您购买了与工厂方法模式相同的东西,那么为什么不直接使用呢?因为工厂方法模式需要创建者类的层次结构来反映正在创建的产品的层次结构;即我们需要一个带有make() 方法的MozzarellaSticksWithKetchupCreator,一个带有make() 方法的HockeyPuckHamburgerWithSoggyFriesCreator,等等。因此,您可以将原型模式简单地视为一种减轻工厂方法模式经常引入的代码冗余的方法。
这个论点来自Design Patterns: Elements of Reusable Object-Oriented Software,又名“四人帮”一书。