设计模式 - 原型模式

原型模式是创建型模式的一种,其特点在于通过 “复制” 一个已经存在的实例来返回新的实例,而不是新建实例。被复制的实例就是我们所称的 “原型”,这个原型是可定制的。

 

类图

 

代码实现

用于测试的 JavaBean:

package com.huey.pattern.prototype;

import java.io.Serializable;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@ToString
@NoArgsConstructor
@AllArgsConstructor
public class TestBean implements Serializable {
    
    /**
     * 
     */
    private static final long serialVersionUID = -2351683706932774662L;
    
    @Getter @Setter
    private String testValue;

}

抽象原型:

package com.huey.pattern.prototype;

/**
 * the abstract prototype
 * @author  huey
 * @version 1.0 
 * @created 2015-11-27
 */
public abstract class Prototype implements Cloneable {
    
    public Prototype() {
        System.out.println("Prototype is constructed.");
    }
    
    @Override
    protected Prototype clone() {
        try {
            return (Prototype) super.clone();
        } catch (Exception e) {
            return null;
        }
    }

}

具体原型:

package com.huey.pattern.prototype;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

/**
 * the concrete prototype
 * @author  huey
 * @version 1.0 
 * @created 2015-11-27
 */
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class ShallowCopyBean extends Prototype {

    @Getter @Setter
    private Integer primitiveValue;
    @Getter @Setter
    private TestBean customValue;
    
}

单元测试:

package com.huey.pattern.prototype;

public class ShallowCopyBeanTest {

    public static void main(String[] args) {        
        Prototype cloneableBean = new ShallowCopyBean(10, new TestBean("AAA"));
        System.out.println("cloneableBean: " + cloneableBean);
        
        ShallowCopyBean clonedBean = (ShallowCopyBean) cloneableBean.clone();
        clonedBean.setPrimitiveValue(100);
        clonedBean.getCustomValue().setTestValue("BBB");
        System.out.println("cloneableBean: " + cloneableBean);
        System.out.println("clonedBean: " + clonedBean);
    }
    
}

结果输出:

Prototype is constructed.
cloneableBean: ShallowCopyBean(primitiveValue=10, customValue=TestBean(testValue=AAA))
cloneableBean: ShallowCopyBean(primitiveValue=10, customValue=TestBean(testValue=BBB))
clonedBean: ShallowCopyBean(primitiveValue=100, customValue=TestBean(testValue=BBB))

从输出可以看到,原型只被构造一次。但是这里需要注意当 clonedBean 的引用对象 customVaule 的内容发生改变时,cloneableBean 的 customVaule 也跟着改变。这就是深拷贝与浅拷贝的问题,Java 中,Object 类的 clone 方法只会拷贝对象中的基本的数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。

 

深拷贝

实现深拷贝可以在重写 clone 方法中,对数组、容器对象、引用对象等自己进行拷贝。

还可以使用一个技巧,就是利用序列化的机制来实现,通过将对象序列化到输出流中,然后将其读回,这样产生的新对象是对现有对象的一个深拷贝。

代码实现

可序列化的抽象原型:

package com.huey.pattern.prototype;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * the abstract prototype
 * @author  huey
 * @version 1.0 
 * @created 2015-11-27
 */
public abstract class SerialCloneable implements Serializable, Cloneable {

    /**
     * 
     */
    private static final long serialVersionUID = -2477239830471073680L;

    @Override
    protected Object clone() {
        try {
            /**
             * serialize the object to the output stream
             */
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            ObjectOutputStream output = new ObjectOutputStream(bout);
            output.writeObject(this);
            output.close();
            
            /**
             * deserialize the object from the input stream
             */
            ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
            ObjectInputStream input = new ObjectInputStream(bin);
            Object result = input.readObject(); 
            input.close();
            
            return result;
        } catch (Exception e) {
            return null;
        }
    }
}

具体原型:

package com.huey.pattern.prototype;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

/**
 * the concrete prototype
 * @author  huey
 * @version 1.0 
 * @created 2015-11-27
 */
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class DeepCopyBean extends SerialCloneable {

    /**
     * 
     */
    private static final long serialVersionUID = -959424582381713341L;
    
    @Getter @Setter
    private Integer primitiveValue;
    @Getter @Setter
    private TestBean customValue;
    
}

单元测试:

package com.huey.pattern.prototype;

public class DeepCopyBeanTest {
    
    public static void main(String[] args) {
        
        SerialCloneable cloneableBean = new DeepCopyBean(10, new TestBean("AAA"));
        System.out.println("cloneableBean: " + cloneableBean);
        
        DeepCopyBean clonedBean = (DeepCopyBean) cloneableBean.clone();
        clonedBean.setPrimitiveValue(100);
        clonedBean.getCustomValue().setTestValue("BBB");
        System.out.println("cloneableBean: " + cloneableBean);
        System.out.println("clonedBean: " + clonedBean);
    }

}

结果输出:

cloneableBean: DeepCopyBean(primitiveValue=10, customValue=TestBean(testValue=AAA))
cloneableBean: DeepCopyBean(primitiveValue=10, customValue=TestBean(testValue=AAA))
clonedBean: DeepCopyBean(primitiveValue=100, customValue=TestBean(testValue=BBB))

从输出可以看到,clonedBean 的引用对象 customVaule 的内容发生改变时,不会影响到 cloneableBean 的 customVaule。

 

原型模式的适用场合

1) 产生对象过程比较复杂,初始化需要许多资源时;

2) 希望框架原型和产生对象分开时;

3) 同一个对象可能会供其他调用者使用访问时。

posted on 2015-11-30 13:16  huey2672  阅读(242)  评论(0编辑  收藏  举报