原型
原型模式用於當要建立的物件型別由一個原型例項確定時,該原型例項被克隆以產生新的物件。此模式用於
- 避免客戶端應用程式中物件建立者的子類,就像抽象工廠模式一樣。
- 避免以標準方式建立新物件時固有的成本(例如,使用“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.net.tw/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
