原型
原型模式用于当要创建的对象类型由一个原型实例确定时,该原型实例被克隆以产生新的对象。此模式用于
- 避免客户端应用程序中对象创建者的子类,就像抽象工厂模式一样。
- 避免以标准方式创建新对象时固有的成本(例如,使用“new”关键字),当对于给定应用程序来说成本过高时。
要实现此模式,请声明一个抽象基类,该基类指定一个纯虚拟的 *clone()* 方法。任何需要“多态构造函数”功能的类都从抽象基类派生,并实现 *clone()* 操作。
客户端,而不是编写调用“new”运算符的代码来硬编码类名,而是调用原型上的 *clone()* 方法,使用指定特定具体派生类的参数调用工厂方法,或通过其他设计模式提供的一些机制调用 *clone()* 方法。
结构
经验法则
有时创建模式会重叠——在某些情况下,原型或抽象工厂都可以适用。在其他时候,它们相互补充:抽象工厂可能会存储一组原型,从中克隆并返回产品对象(GoF,第 126 页)。抽象工厂、建造者和原型可以在其实现中使用单例。(GoF,第 81、134 页)。抽象工厂类通常使用工厂方法(通过继承创建)实现,但也可以使用原型(通过委托创建)实现。(GoF,第 95 页)
通常,设计从使用工厂方法(不太复杂,更可定制,子类激增)开始,并随着设计师发现需要更多灵活性的位置而发展为抽象工厂、原型或建造者(更灵活,更复杂)。(GoF,第 136 页)
原型不需要子类化,但它确实需要一个“初始化”操作。工厂方法需要子类化,但不需要初始化。(GoF,第 116 页)
大量使用组合和装饰器模式的设计通常也能从原型中受益。(GoF,第 126 页)
经验法则是,当您想要在 *运行时* 创建另一个 *真实副本* 的 Object 时,您需要克隆一个 *Object*。*真实副本* 表示新创建的 Object 的所有属性都应与您正在克隆的 Object 相同。如果您可以使用 *new* 而不是 *实例化* 类,您将获得一个具有所有属性作为其初始值的 Object。例如,如果您正在设计一个用于执行银行账户交易的系统,那么您可能希望复制包含您账户信息的 Object,在其上执行交易,然后用修改后的 Object 替换原始 Object。在这种情况下,您可能希望使用 clone() 而不是 new。
建议
- 在原型类的名称中放入 *prototype* 术语,以向其他开发人员表明该模式的使用。
- 只有在必要时才使用接口。
实现
它指定使用原型实例来创建哪种对象。新产品的原型通常在全面生产之前构建,但在本例中,原型是无源的,不参与复制自身。细胞的有丝分裂——导致两个相同的细胞——是原型在复制自身中起积极作用的例子,因此,它展示了原型模式。当细胞分裂时,会产生两个基因型相同的细胞。换句话说,细胞克隆了自己。
对象的确切类型是从其原型创建的。MemberwiseClone 在 Clone 方法中用于创建和返回 ConcreteFoo1 或 ConcreteFoo2 的副本。
public abstract class Foo
{
// normal implementation
public abstract Foo Clone();
}
public class ConcreteFoo1 : Foo
{
public override Foo Clone()
{
return (Foo)this.MemberwiseClone(); // Clones the concrete class.
}
}
public class ConcreteFoo2 : Foo
{
public override Foo Clone()
{
return (Foo)this.MemberwiseClone(); // Clones the concrete class.
}
}
另一个例子
//Note: In this example ICloneable interface (defined in .Net Framework) acts as Prototype
class ConcretePrototype : ICloneable
{
public int X { get; set; }
public ConcretePrototype(int x)
{
this.X = x;
}
public void PrintX()
{
Console.WriteLine("Value :" + X);
}
public object Clone()
{
return this.MemberwiseClone();
}
}
/**
* Client code
*/
public class PrototypeTest
{
public static void Main()
{
var prototype = new ConcretePrototype(1000);
for (int i = 1; i < 10; i++)
{
ConcretePrototype tempotype = prototype.Clone() as ConcretePrototype;
// Usage of values in prototype to derive a new value.
tempotype.X *= i;
tempotype.PrintX();
}
Console.ReadKey();
}
}
/*
**Code output**
Value :1000
Value :2000
Value :3000
Value :4000
Value :5000
Value :6000
Value :7000
Value :8000
Value :9000
*/
#include <iostream>
using namespace std;
// Prototype
class Prototype
{
public:
virtual ~Prototype() { }
virtual Prototype* clone() const = 0;
virtual void setX(int x) = 0;
virtual int getX() const = 0;
virtual void printX() const = 0;
};
// Concrete prototype
class ConcretePrototype : public Prototype
{
public:
ConcretePrototype(int x) : x_(x) { }
ConcretePrototype(const ConcretePrototype& p) : x_(p.x_) { }
virtual ConcretePrototype* clone() const { return new ConcretePrototype(*this); }
void setX(int x) { x_ = x; }
int getX() const { return x_; }
void printX() const { std::cout << "Value :" << x_ << std::endl; }
private:
int x_;
};
// Client code
int main()
{
Prototype* prototype = new ConcretePrototype(1000);
for (int i = 1; i < 10; i++) {
Prototype* temporaryPrototype = prototype->clone();
temporaryPrototype->setX(temporaryPrototype->getX() * i);
temporaryPrototype->printX();
delete temporaryPrototype;
}
delete prototype;
return 0;
}
代码输出
Value :1000 Value :2000 Value :3000 Value :4000 Value :5000 Value :6000 Value :7000 Value :8000 Value :9000
此模式使用其原型创建对象类型。换句话说,在创建 Prototype 对象时,类会创建它的克隆,并将其作为原型返回。clone 方法已用于在需要时克隆原型。
// Prototype pattern
public abstract class Prototype implements Cloneable {
public Prototype clone() throws CloneNotSupportedException{
return (Prototype) super.clone();
}
}
public class ConcretePrototype1 extends Prototype {
@Override
public Prototype clone() throws CloneNotSupportedException {
return (ConcretePrototype1)super.clone();
}
}
public class ConcretePrototype2 extends Prototype {
@Override
public Prototype clone() throws CloneNotSupportedException {
return (ConcretePrototype2)super.clone();
}
}
另一个例子
/**
* Prototype class
*/
interface Prototype extends Cloneable {
void setX(int x);
void printX();
int getX();
}
/**
* Implementation of prototype class
*/
class PrototypeImpl implements Prototype {
private int x;
/**
* Constructor
*/
public PrototypeImpl(int x) {
setX(x);
}
@Override
public void setX(int x) {
this.x = x;
}
@Override
public void printX() {
System.out.println("Value: " + getX());
}
@Override
public int getX() {
return x;
}
@Override
public PrototypeImpl clone() throws CloneNotSupportedException {
return (PrototypeImpl) super.clone();
}
}
/**
* Client code
*/
public class PrototypeTest {
public static void main(String args[]) throws CloneNotSupportedException {
PrototypeImpl prototype = new PrototypeImpl(1000);
for (int y = 1; y < 10; y++) {
// Create a defensive copy of the object to allow safe mutation
Prototype tempotype = prototype.clone();
// Derive a new value from the prototype's "x" value
tempotype.setX(tempotype.getX() * y);
tempotype.printX();
}
}
}
代码输出
Value: 1000 Value: 2000 Value: 3000 Value: 4000 Value: 5000 Value: 6000 Value: 7000 Value: 8000 Value: 9000
// The Prototype pattern in PHP is done with the use of built-in PHP function __clone()
abstract class Prototype
{
public string $a;
public string $b;
public function displayCONS(): void
{
echo "CONS: {$this->a}\n";
echo "CONS: {$this->b}\n";
}
public function displayCLON(): void
{
echo "CLON: {$this->a}\n";
echo "CLON: {$this->b}\n";
}
abstract function __clone();
}
class ConcretePrototype1 extends Prototype
{
public function __construct()
{
$this->a = "A1";
$this->b = "B1";
$this->displayCONS();
}
function __clone()
{
$this->displayCLON();
}
}
class ConcretePrototype2 extends Prototype
{
public function __construct()
{
$this->a = "A2";
$this->b = "B2";
$this->displayCONS();
}
function __clone()
{
$this->a = $this->a ."-C";
$this->b = $this->b ."-C";
$this->displayCLON();
}
}
$cP1 = new ConcretePrototype1();
$cP2 = new ConcretePrototype2();
$cP2C = clone $cP2;
// RESULT: #quanton81
// CONS: A1
// CONS: B1
// CONS: A2
// CONS: B2
// CLON: A2-C
// CLON: B2-C
另一个例子
<?php
class ConcretePrototype {
protected $x;
public function __construct($x) {
$this->x = (int) $x;
}
public function printX() {
echo sprintf('Value: %5d' . PHP_EOL, $this->x);
}
public function setX($x) {
$this->x *= (int) $x;
}
public function __clone() {
/*
* This method is not required for cloning, although when implemented,
* PHP will trigger it after the process in order to permit you some
* change in the cloned object.
*
* Reference: https://php.ac.cn/manual/en/language.oop5.cloning.php
*/
// $this->x = 1;
}
}
/**
* Client code
*/
$prototype = new ConcretePrototype(1000);
foreach (range(1, 10) as $i) {
$tempotype = clone $prototype;
$tempotype->setX($i);
$tempotype->printX();
}
/*
**Code output**
Value: 1000
Value: 2000
Value: 3000
Value: 4000
Value: 5000
Value: 6000
Value: 7000
Value: 8000
Value: 9000
Value: 10000
*/
让我们为文本编写一个出现浏览器类。此类列出文本中某个单词出现的次数。此类对象的创建成本很高,因为出现位置的查找需要一个昂贵的过程。因此,为了复制这样的对象,我们使用原型模式
class WordOccurrences is field occurrences is The list of the index of each occurrence of the word in the text. constructor WordOccurrences(text, word) is input: the text in which the occurrences have to be found input: the word that should appear in the text Empty the occurrences list for each Template:Not a typo in text Template:Not a typog:= true for each Template:Not a typo in word if the current word character does not match the current text character then Template:Not a typog:= false if Template:Not a typo is true then Add the current Template:Not a typo into the occurrences list method getOneOccurrenceIndex(n) is input: a number to point on the nth occurrence. output: the index of the nth occurrence. Return the nth item of the occurrences field if any. method clone() is output: a WordOccurrences object containing the same data. Call clone() on the super class. On the returned object, set the occurrences field with the value of the local occurrences field. Return the cloned object. texte:= "The prototype pattern is a creational design pattern in software development first described in design patterns, the book." wordw:= "pattern"d searchEnginen:= new WordOccurrences(text, word) anotherSearchEngineE:= searchEngine.clone()
(搜索算法未优化;它是一个基本算法,用于说明模式实现)
此实现使用 装饰器 模式。
# Decorator class which allows an object to create an exact duplicate of itself
class Prototype:
def _clone_func(self):
# This function will be assigned to the decorated object and can
# be used to create an exact duplicate of that decorated object
clone = self.cls()
# Call _copy_func to ensure the attributes of the objects are identical
self._copy_func(self.instance, clone)
return clone
def _copy_func(self, fromObj, toObj):
# Dual purpose function which is assigned to the decorated object
# and used internally in the decorator to copy the original attributes
# to the clone to ensure it's identical to the object which made the clone
for attr in dir(fromObj):
setattr(toObj, attr, getattr(fromObj, attr))
def __init__(self, cls):
# This is only called once per decorated class type so self.cls
# should remember the class that called it so it can generate
# unique objects of that class later on (in __call__)
self.cls = cls
# NOTE: on a decorator "__call__" MUST be implemented
# this function will automatically be called each time a decorated
# object is cloned/created
def __call__(self):
# Create an instance of the class here so a unique object can be
# sent back to the constructor
self.instance = self.cls()
# Assign the private functions back to the unique class
# (which is the whole point of this decorator)
self.instance.Clone = self._clone_func
self.instance.Copy = self._copy_func
# Finally, send the unique object back to the object's constructor
return self.instance
@Prototype
class Concrete:
def __init__(self):
# Test value to see if objects are independently generated as
# well as if they properly copy from one another
self.somevar = 0
@Prototype
class another:
def __init__(self):
self.somevar = 50
if __name__ == '__main__':
print "Creating A"
a = Concrete()
print "Cloning A to B"
b = a.Clone()
print "A and B somevars"
print a.somevar
print b.somevar
print "Changing A somevar to 10..."
a.somevar = 10
print "A and B somevars"
print a.somevar
print b.somevar
print "Creating another kind of class as C"
c = another()
print "Cloning C to D"
d = c.Clone()
print "Changing C somevar to 100"
c.somevar = 100
print "C and D somevars"
print c.somevar
print d.somevar
输出
Creating A Cloning A to B A and B somevars 0 0 Changing A somevar to 10... A and B somevars 10 0 Creating another kind of class as C Cloning C to D Changing C somevar to 100 C and D somevars 100 50