天天看點

設計模式之原型模式(建立型)

[TOC]

模式定義

原型模式(Prototype Pattern):原型模式是提供一個原型接口,提供原型的克隆,建立新的對象,是一種對象建立型模式。

模式結構

原型模式包括如下角色

  • Prototype :抽象原型類
  • ConcretePrototype:具體原型類
  • Client:客戶類

原型模式類别

一個類包括另外一個成員變量,在使用原型模式進行對象克隆時,如果直接是通過super Cloneable接口的的clone方法,這種情況其實并不支援類中另外一些成員變量的克隆的,這種方法稱之為淺克隆,是以淺克隆和深克隆的本質差別就是看其是否支援類中的成員變量的克隆。

綜上,原型模式可以淺克隆和深克隆兩種情況,其差別是是否支援類中的成員變量的克隆。

原型模式的淺克隆

原型模式在Java裡的常用實作是通過類繼承 JDK提供的Cloneable接口,重寫 clone(),這種方法其實也可以稱之為原型模式的淺克隆

public class A implements Cloneable 
{

    
    public Object clone()
    {
        A clone=null;
        try
        {
            clone=(A)super.clone();     
        }
        catch(CloneNotSupportedException e)
        {
            System.out.println("Clone failure!");
        }
        return clone;
    }
}      

一般來說,clone方法符合:

  • 類型相同:對于任何對象a,a.clone().getClass() = a.getClass()
  • 記憶體位址不同:也可以說對于任何對象a,a.clone()!=a,克隆對象和原對象不是同一個對象
  • a對象的equals方法:對于任何對象a,a.clone().equals(a)

淺克隆的例子,例子來自​​《設計模式》​​一書的郵件複制

由于郵件對象包含的内容較多(如發送者、接收者、标題、内容、日期、附件等),某系統中現需要提供一個郵件複制功能,對于已經建立好的郵件對象,可以通過複制的方式建立一個新的郵件對象,如果需要改變某部分内容,無須修改原始的郵件對象,隻需要修改複制後得到的郵件對象即可。使用原型模式設計該系統。在本執行個體中使用淺克隆實作郵件複制,即複制郵件(Email)的同時不複制附件(Attachment)。

附件類:

public class Attachment
{
    public void download()
    {
        System.out.println("下載下傳附件"); 
    }
}      

郵件類,淺克隆:

public class Email implements Cloneable 
{
    private Attachment attachment=null;
    
    public Email()
    {
        this.attachment=new Attachment();
    }
    
    public Object clone()
    {
        Email clone=null;
        try
        {
            clone=(Email)super.clone();     
        }
        catch(CloneNotSupportedException e)
        {
            System.out.println("Clone failure!");
        }
        return clone;
    }
    
    public Attachment getAttachment()
    {
        return this.attachment;
    }
    
    public void display()
    {
        System.out.println("檢視郵件"); 
    }
    
}      

用戶端類:

public class Client
{
    public static void main(String a[])
    {
        Email email,copyEmail;
        
        email=new Email();
        
        copyEmail=(Email)email.clone();
        
        System.out.println("email==copyEmail?");
        System.out.println(email==copyEmail);
        
        System.out.println("email.getAttachment==copyEmail.getAttachment?"); 
        System.out.println(email.getAttachment()==copyEmail.getAttachment());           
    }
}      

編譯傳回,第一個是false,第二個是true,由前面的理論可以知道,淺克隆對于成員變量是不支援克隆的,因為對象位址還是一樣的

原型模式的深克隆

上面是淺克隆的實作,對于原型模式深克隆的實作一般是提供類的序列化來實作

附件類,注意要implements Serializable

import java.io.*;

public class Attachment implements Serializable
{
    public void download()
    {
        System.out.println("下載下傳附件");
    }
}      

郵件類,同樣要實作Serializable接口

import java.io.*;

public class Email implements Serializable
{
    private Attachment attachment=null;

    public Email()
    {
        this.attachment=new Attachment();
    }

    public Object deepClone() throws IOException, ClassNotFoundException, OptionalDataException
    {
        //将對象寫入流中
        ByteArrayOutputStream bao=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(bao);
        oos.writeObject(this);

        //将對象從流中取出
        ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray());
        ObjectInputStream ois=new ObjectInputStream(bis);
        return(ois.readObject());
    }

    public Attachment getAttachment()
    {
        return this.attachment;
    }

    public void display()
    {
        System.out.println("檢視郵件");
    }

}      

用戶端類:

public class Client
{
    public static void main(String a[])
    {
        Email email,copyEmail=null;

        email=new Email();

        try{
            copyEmail=(Email)email.deepClone();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }


        System.out.println("email==copyEmail?");
        System.out.println(email==copyEmail);

        System.out.println("email.getAttachment==copyEmail.getAttachment?");
        System.out.println(email.getAttachment()==copyEmail.getAttachment());
    }
}      

編譯傳回,第一個是false,第二個是flase,由前面的理論可以知道,深克隆對于成員變量是支援克隆的,因為對象位址是一樣的

import java.util.*;

interface MyColor extends Cloneable
{
    public Object clone();
    public void display();
}

class Red implements MyColor
{
   public Object clone()
   {
     Red r=null;
     try
     {
       r=(Red)super.clone();
     }
     catch(CloneNotSupportedException e)
     {  
  
     }
     return r;
   }
   public void display()
   {
     System.out.println("This is Red!");
   }
}

class Blue implements MyColor
{
   public Object clone()
   {
     Blue b=null;
     try
     {
       b=(Blue)super.clone();
     }
     catch(CloneNotSupportedException e)
     {  
  
     }
     return b;
   }
   public void display()
   {
     System.out.println("This is Blue!");
   }
}

class PrototypeManager 
{
   private Hashtable ht=new Hashtable();
   
   public PrototypeManager()
   {
      ht.put("red",new Red());
      ht.put("blue",new Blue());
   }
   
   public void addColor(String key,MyColor obj)
   {
      ht.put(key,obj);
   }
   
   public MyColor getColor(String key)
   {
      return (MyColor)((MyColor)ht.get(key)).clone();
   }
}

class Client
{
   public static void main(String args[])
   {
      PrototypeManager pm=new PrototypeManager();  
      
      MyColor obj1=(MyColor)pm.getColor("red");
      obj1.display();
      
      MyColor obj2=(MyColor)pm.getColor("red");
      obj2.display();
      
      System.out.println(obj1==obj2);
   }
}      

模式應用

  • 儲存對象的狀态:對于要儲存的狀态不是很占記憶體的情況,可以适用原型模式和備忘錄模式儲存對象狀态,如果對象占用太多記憶體,那就還是狀态模式比較好
  • 建立新對象成本很大的情況:比如建立一個對象是需要查詢很慢的SQL才能給對象指派,這種情況就和适合用原型模式克隆對象,減少對象建立和查詢
  • 對于很多軟體的複制和粘貼實作其實也是原型模式的應用
  • Spring架構提供BeanUtils.copyProperties方法也是原型模式的應用

繼續閱讀