### 一、說說鳴人的影分身
話說鳴人聽了水木老師的建議偷出了卷軸并且學會了一招禁術:影分身之術。當鳴人使用影分身之術的時候就會有好多個和鳴人一模一樣的人出現,就像復制出來的一樣,這種影分身之術在面向對象的設計領域里就叫做原型模式。
### 二、什么是原型模式
有了上邊的鳴人的例子,我們再理解圓形模式的定義應該會更簡單了,GOF給它的定義是:用原型實例指定創建對象的種類并且通過拷貝這些原型對象創建新的對象。
在Java中提供了clone()方法來實現對象的克隆,所以原型模式(Prototype)實現變得簡單的多了。
### 三、再來說說clone()方法
Java的所有類 都是從java.lang.Object類繼承而來的,而Object類提供下面的方法對對象進行復制:
`protected Object clone()`
子類也可以將這個方法覆蓋掉,用自己的邏輯實現自己的復制方法。可以被使用clone()方法的類都必須實現Cloneable接口,Cloneable接口只起一個作用就是在運行時期通知Java虛擬機可以安全地在這個類上使用clone方法。
克隆又分為兩種:淺克隆和深度克隆
**淺度克隆:**

如上圖所示,淺度復制只是復制對象的值,我們知道對象的屬性一共分為兩種,基本類型和引用類型,對于淺度復制基本類型的數據會復制一份到新的對象中去,對于引用類型的屬性僅僅復制引用的值,引用所指向的具體的對象不會復制,所以A和B實際上是用的同一個對象c,如果再A中改變c的屬性,B中也能看到,因為改變的是兩者共有對象。Java提供的clone方法就是這種類型的。

深度復制與淺度復制的不同就是深度復制不但會復制對象的引用,并且還會復制引用所指的對象。所以在第二幅圖中A和B是指向的不同的對象,此時在A中操作c對象不會對B產生任何影響。
**克隆滿足的條件:**
1、對任何對象x,都有:x.clone()!=x.換言之,克隆對象與原來的對象不是同一個對象。
2、對任何對象x,都有:x.clone().getClass==x.getClass(),換言之,克隆對象與原對象的類型一致。
3、如果對象x的equals()方法定義恰當的話,那么x.clone().equals(x)應該是成立的。
**關于equals方法的說明:**被克隆的對象按照他們的內部狀態是否可變,劃分為可變對象和不可變對象(String的內部數值是不能改變的)。對于可變對象只有當他們是同一個對象時才會返回true,而對于不變對象,當他們的內部狀態值是一樣的時候就認為是true,但是內部狀態一直的不一定就是同一個對象。
### 四、原型模式的結構
原型模式模式分為兩種,一種是不帶管理類的原型模式,另一種是帶管理類的原型模式。
下面這種是不帶管理類的原型模式:

這種形式涉及到三個角色:
客戶角色:客戶提出創建對象的請求。
抽象原型角色:這是一個抽象角色,通常由一個Java接口或Java抽象類實現,這個類可能會繼承Cloneable接口。
具體原型角色:被復制的對象。此角色需要實現抽象的原型角色所要求的接口。
我們通過一個實例來看一下具體的使用過程。
我們舉一個大學里常見的例子,一個班里有一個學霸的話整個班級的作業就不用愁了,大家可以拿學霸的作業去復制嘛。
這個類是作業的抽象父類,定義了一些作業都要實現的方法,這里只實現了一個數學作業類,將來可以能有編程作業等。
~~~
package com.designpattern.prototype1;
public abstract class Homework implements Cloneable {
public abstract Object clone();
public abstract void show();
}
~~~
數學作業的類要實現自己的復制邏輯,因為數學作業和編程作業的抄襲的方法肯定是不一樣的。
~~~
package com.designpattern.prototype1;
import java.util.Date;
public class MathHomework extends Homework{
/**
* 這里只是用一個日期類來表示一下深度復制
*/
private Date A = new Date();
private int a = 1;
public void show() {
System.out.println("Math clone");
}
/**
* 實現自己的克隆方法
*/
public Object clone(){
MathHomework m = null;
/**
* 深度復制
*/
m = (MathHomework) this.clone();
m.A = (Date)this.getA().clone();
return m;
}
public Date getA(){
return A;
}
}
~~~
客戶端就可以使用學霸的作業抄襲了
~~~
package com.designpattern.prototype1;
public class Main {
public static void main(String[] args){
/**
* 建立一個學霸,全班同學的作業就靠他了
*/
MathHomework xueba = new MathHomework();
/**
* 學渣都是從學霸那復制來的
*/
MathHomework xuezha = (MathHomework)xueba.clone();
xuezha.show();
}
}
~~~
那如果一個班里有兩個學霸呢,那肯定班里的同學有的會超A同學的,有的會抄B同學的,這樣的話系統里就必須要保留兩個原型類,這時候使用我們的帶有管理類的原型模式就比較方便了。
此時的結構圖是這樣的:

新增加的管理類:
~~~
package com.designpattern.prototype1;
import java.util.Map;
public class Manager {
private static Manager manager;
private Map prototypes = null;
private Manager() {
manager = new Manager();
}
//使用了簡單工廠模式
public static Manager getManager() {
if (manager == null)
manager = new Manager();
return manager;
}
public void put(String name,Homework prototype){
manager.put(name, prototype);
}
public Homework getPrototype(String name){
if(prototypes.containsKey(name)){
return (Homework) ((Homework)prototypes.get(name)).clone();
}else{
Homework homework = null;
try{
homework = (Homework)Class.forName(name).newInstance();
put(name, homework);
}catch(Exception e){
e.printStackTrace();
}
return homework;
}
}
}
~~~
~~~
package com.designpattern.prototype1;
public class MainManager {
public static void main(String[] args){
/**
* 建立一個學霸,全班同學的作業就靠他了
*/
MathHomework xueba = new MathHomework();
Manager.getManager().put("com.designpattern.prototype1.MathHomework", xueba);
/**
* 學渣都是從學霸那復制來的
*/
MathHomework xuezha = (MathHomework) Manager.getManager().getPrototype("com.designpattern.prototype1.MathHomework");
xuezha.show();
}
}
~~~
簡單形式和登記形式的原型模式各有其長處和短處,如果需要創建的原型對象數目較少 而且比較固定的話可以采取簡單形式,如果創建的原型對象數目不固定的話建議采取第二種形式。
### 五、原型模式的優缺點
**優點:**
1、將產品的創建過程封裝起來,客戶端不需要了解產品的具體創建流程。
2、利用Java的clone方法來創建對象肯定要比使用new來創建對象快很多,尤其是那些很復雜的對象的時候。
3、可以在不修改其他代碼的情況下添加新的產品,符合“開-閉”原則。
**缺點:**原型模式的最大缺點就是每一個類必須都有一個clone方法,如果這個類的組成不太復雜的話還比較好,如果類的組成很復雜的話,如果想實現深度復制就非常困難了。
### 六、原型模式的選擇
假設一個系統的產品類是動態加載的,而且產品類具有一定的等級結構。這個時候如果采用工廠模式的話,工廠類就不得不具有一個相應的等級結構。而產品類的等級結構一旦發生變化,工廠類的等級結構就不得不有一個相應的變化,這對于產品結構可能經常變化的系統來說采用工廠模式是很不方便的。這個時候如果采用原型模式,給每個產品類裝配一個clone方法便可以避免使用工廠方式所帶來的具有固定等級結構的工廠類。
源碼下載:[http://download.csdn.net/detail/xingjiarong/9308023](http://download.csdn.net/detail/xingjiarong/9308023)
- 前言
- 設計原則(一)"開-閉"原則(OCP)
- 設計原則(二)里氏替換原則(LSP)
- 設計原則(三)組合復用原則
- 設計原則(四)依賴倒置原則(DIP)
- 設計模式(一)簡單工廠模式
- 設計模式(二)工廠方法模式
- 設計模式(三)抽象工廠模式
- 設計模式(四)單例模式
- 設計模式(五)創建者模式(Builder)
- 設計模式(六)原型模式
- 設計模式(七)門面模式(Facade Pattern 外觀模式)
- 設計模式(八)橋梁模式(Bridge)
- 設計模式(九)裝飾模式(Decorator)
- 設計模式(十)適配器模式
- 設計模式(十一)策略模式
- 設計模式(十二)責任鏈模式
- 設計模式之UML(一)類圖以及類間關系(泛化 、實現、依賴、關聯、聚合、組合)
- 設計模式之橋梁模式和策略模式的區別