设计模式之原型模式(创建型)详解架构师

模式定义

原型模式(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方法也是原型模式的应用

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/tech/architecture/7122.html

(0)
上一篇 2021年7月17日 01:53
下一篇 2021年7月17日 01:53

相关推荐

发表回复

登录后才能评论