創建型模式【3】——原型模式

 


1 原型模式特點

  • 定義 : 指原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象
  • 特點 : 不需要知道任何創建的細節,不調用構造函數
  • 類型 : 創建型

適用場景

  • 類初始化消耗較多資源
  • new產生的一個對象需要非常繁瑣的過程(數據準備、訪問權限等)
  • 構造函數比較複雜
  • 循環體中生產大量對象時
  • 優點 : 原型模式性能比直接new一個對象性能高;簡化創建過程
  • 缺點 : 必須配備克隆方法;對克隆複雜對象或對克隆出的對象進行復雜改造時,容易引入風險;深拷貝、淺拷貝要運用得當

2 Coding

現在有一隻羊tom(姓名爲:tom, 年齡爲:1,顏色爲:白色),請編寫程序創建和tom羊屬性完全相同的10只羊

public class Sheep {
    //名字
    private String name;
    //年齡
    private int age;
    //顏色
    private String color;
    public Sheep(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }
    //省略 get set toString 方法
//public class Client {
//    @Test
//    public void test() {
//        //傳統的方法
//        Sheep sheep = new Sheep("tom", 1, "白色");
//        Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
//        Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
//        Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
//        Sheep sheep5 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
//        //....
//        System.out.println(sheep);
//        System.out.println(sheep2);
//        System.out.println(sheep3);
//        System.out.println(sheep4);
//        System.out.println(sheep5);
//        //...
//    }
//}
//實現Cloneable
public class Sheep implements Cloneable{
	private String name;
	private int age;
	private String color;
	public Sheep(String name, int age, String color) {
		this.name = name;
		this.age = age;
		this.color = color;
	}
	//重寫clone
	@Override
	protected Sheep clone() throws CloneNotSupportedException {
		//使用默認的clone方法來完成
		return (Sheep)super.clone();
	}
	//省略 get set toString 方法
//public class Client {
//    @Test
//    public void test() throws CloneNotSupportedException {
//        System.out.println("原型模式完成對象的創建");
//        Sheep sheep = new Sheep("tom", 1, "白色");
//        Sheep sheep2 = sheep.clone(); //克隆
//        Sheep sheep3 = sheep.clone(); //克隆
//        Sheep sheep4 = sheep.clone(); //克隆
//        Sheep sheep5 = sheep.clone(); //克隆
//        System.out.println("sheep2 =" + sheep2);
//        System.out.println("sheep3 =" + sheep3);
//        System.out.println("sheep4 =" + sheep4);
//        System.out.println("sheep5 =" + sheep5);
//    }
//}

 

//抽象類
public abstract class A implements Cloneable {
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class B extends A {
    public static void main(String[] args) throws CloneNotSupportedException {
        B b = new B();
        B clone = (B) b.clone();
        System.out.println(b); // B@6ff3c5b5
        System.out.println(clone); // B@3764951d
    }
}

3 原型模式擴展

3.1 淺克隆錯誤演示

public class Pig implements Cloneable {
    private String name;
    private Date birthday;
    public Pig(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }
    public String getName() {return name;}
    public void setName(String name) {this.name = name; }
    public Date getBirthday() {return birthday; }
    public void setBirthday(Date birthday) {this.birthday = birthday;}
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    @Override
    public String toString() {
        return "Pig{" + "name='" + name + '\'' + ", birthday=" + birthday + '}' + super.toString();
    }
}
public class PigTest {
    @Test
    public void test() throws CloneNotSupportedException {
        Date birthday = new Date(0L);
        Pig pig = new Pig("佩奇", birthday);
        Pig cPig = (Pig) pig.clone();
        System.out.println(pig); //  Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}Pig@66cd51c3
        System.out.println(cPig); // Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}Pig@4dcbadb4
        //本意只是修改pig的生日
        pig.getBirthday().setTime(666666666666L);
        System.out.println(pig); //            Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}Pig@66cd51c3
        //cPig也改了,這個結果不是我想要的,原因出在淺拷貝上
        System.out.println(cPig); // 跟着改了 : Pig{name='佩奇', birthday=Sat Feb 16 09:11:06 CST 1991}Pig@4dcbadb4
    }

3.2 深克隆兩種實現方式

public class Pig implements Cloneable, Serializable {
    private static final long serialVersionUID = 779970270042384579L;
    private String name;
    private Date birthday;
    public Pig(String name, Date birthday) {this.name = name;this.birthday = birthday;}
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}
    public Date getBirthday() {return birthday;}
    public void setBirthday(Date birthday) {this.birthday = birthday;}
    //深拷貝——使用clone方法
    //Remove this "clone" implementation; use a copy constructor or copy factory instead.
    @Override
    protected Pig clone() throws CloneNotSupportedException {
        Pig pig = (Pig) super.clone();
        pig.birthday = (Date) pig.birthday.clone();
        return pig;
    }
    //深拷貝——通過對象的序列化實現 (推薦)
    public Pig deepClone() {
        //創建流對象
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try {
            //序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this); //當前這個對象以對象流的方式輸出
            //反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            return (Pig) ois.readObject();
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        } finally {
            //關閉流
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (Exception e) {

            }
        }
    }
    @Override
    public String toString() {
        return "Pig{" + "name='" + name + '\'' + ", birthday=" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(birthday) + '}' + " " + super.toString();
    }
}
public class PigTest {
    @Test
    public void test() {
        Date birthday = new Date(0L);
        Pig pig = new Pig("佩奇", birthday);
        //Pig cPig = pig.clone();
        Pig cPig = pig.deepClone();
        System.out.println(pig); //  Pig{name='佩奇', birthday=1970/01/01 08:00:00} Pig@1b9e1916
        System.out.println(cPig); // Pig{name='佩奇', birthday=1970/01/01 08:00:00} Pig@61064425
        pig.getBirthday().setTime(666666666666L);
        System.out.println(pig); //  Pig{name='佩奇', birthday=1991/02/16 09:11:06} Pig@1b9e1916
        System.out.println(cPig); // Pig{name='佩奇', birthday=1970/01/01 08:00:00} Pig@61064425
    }
}


4 原型模式破壞單例模式

單例模式實現了Cloneable一定要小心

//實現Serializable和Cloneable接口
public class Singleton implements Cloneable {
    private final static Singleton instance = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return instance;
    }
    @Override
    protected Singleton clone() {
        //return (Singleton) super.clone();
        //注意,返回的是靜態變量,不要調用父類方法然後返回
        return instance;
    }
}
/**
 * 原型模式裏面的克隆可能會破壞單例模式
 */
@Test
public void test() throws Exception {
    Singleton hungrySingleton = Singleton.getInstance();
    Singleton cloneHungrySingleton = hungrySingleton.clone();
    System.out.println(hungrySingleton);//
    System.out.println(cloneHungrySingleton);//
}

5 源碼分析 

public class HungrySingleton implements Serializable, Cloneable {
    private final static HungrySingleton hungrySingleton;
    static {
        hungrySingleton = new HungrySingleton();
    }
    private HungrySingleton() {
        if (hungrySingleton != null) {
            throw new RuntimeException("單例構造器禁止反射調用");
        }
    }
    public static HungrySingleton getInstance() {
        return hungrySingleton;
    }
    private Object readResolve() {
        return hungrySingleton;
    }
    //
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return getInstance();
    }
}
@Test
public void test() throws CloneNotSupportedException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    HungrySingleton hungrySingleton = HungrySingleton.getInstance();
    Method method = hungrySingleton.getClass().getDeclaredMethod("clone");
    method.setAccessible(true);
    HungrySingleton cloneHungrySingleton = (HungrySingleton) method.invoke(hungrySingleton);
    System.out.println(hungrySingleton);//HungrySingleton@66cd51c3
    System.out.println(cloneHungrySingleton);//HungrySingleton@66cd51c3
}

https://www.jianshu.com/p/54b0711a8ec8 

//java.lang.Cloneable
public interface Cloneable {

//java.util.ArrayList
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
    public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }
//<dependency>
//    <groupId>org.mybatis</groupId>
//    <artifactId>mybatis</artifactId>
//    <version>3.5.4</version>
//</dependency>

//org.apache.ibatis.cache.CacheKey
public class CacheKey implements Cloneable, Serializable {
  @Override
  public CacheKey clone() throws CloneNotSupportedException {
    CacheKey clonedCacheKey = (CacheKey) super.clone();
    clonedCacheKey.updateList = new ArrayList<>(updateList);
    return clonedCacheKey;
  }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章