設計模式五: 原型模式(Prototype)

簡介

原型模式是屬於建立型模式的一種,是經過拷貝原型對象來建立新的對象.html

萬能的Java超類Object提供了clone()方法來實現對象的拷貝.java

能夠在如下場景中使用原型模式:api

  1. 構造函數建立對象成本太大(性能或安全成本)
  2. 要保存對象的狀態, 且狀態變化較小, 不會過多佔用內存時(狀態變化較大的使用狀態模式會更合適)

意圖

使用原型實例指定要建立的對象類型,並經過拷貝這個原型來建立新對象。安全

類圖

原型模式

實現

一. 淺拷貝和深拷貝的概念oracle

Object.clone()方法實現的是對象的淺拷貝, 所謂淺拷貝就是當對象中有複雜引用類型的域變量時, 只拷貝該域變量的引用而不是內容, 當有任一方法修改域變量的狀態時會同時影響原型對象及拷貝對象, 實際上他們共用了同一個堆內存. 深拷貝建立的對象便是對原對象的徹底拷貝,對任一對象的操做不會影響其餘對象的狀態.函數

java中提供了Cloneable接口, 約定實現接口Cloneable且重寫Object.clone()方法的類能夠用來拷貝自身. Cloneable是一個標記接口, 其中沒有定義任何方法.性能

二. 下面的代碼演示了使用clone()方法實現的深拷貝,這種方式更適合用於比較簡單的對象,不然clone()方法的實現可能會變得異常複雜.this

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class CarProperty implements Cloneable {

    private String power;
    private double maxSpeed;
    private double oilPerKm;

    public Object clone(){
        Object obj = null;
        try {
            obj = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return obj;
    }
}
import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Car implements Cloneable {

    private String brand;
    private double price;
    private CarProperty carProperty;

    /**
     * 深拷貝在此實現,對於複雜的應用類型, 這裏的代碼可能會至關複雜,若是類有修改(新增成員變量等),這裏也須要相應修改
     * @return
     */
    public Object clone(){
        Object car = null;
        try {
            car = super.clone();
            CarProperty carPropertyClone = (CarProperty)this.getCarProperty().clone();
            ((Car)car).setCarProperty(carPropertyClone);
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return car;
    }

    public static void main(String[] args) {

        CarProperty carProperty = new CarProperty("8匹",250,30);
        Car car= new Car("BMW",200,carProperty);

        Car copy = (Car) car.clone();
        System.out.println("copy最大速度爲: "+copy.getCarProperty().getMaxSpeed());
        System.out.println("原型最大速度爲: "+car.getCarProperty().getMaxSpeed());
        car.getCarProperty().setMaxSpeed(360);
        System.out.println("copy最大速度爲: "+copy.getCarProperty().getMaxSpeed());
        System.out.println("原型最大速度爲: "+car.getCarProperty().getMaxSpeed());
    }

}

三. 深拷貝的其餘實現方式: 除了上面的方法,還能夠使用反射機制建立對象的深拷貝, 另一種更簡單的方式是使用序列化;
下面的代碼使用序列化方式實現對象的深拷貝,需實現Serializable接口.code

import java.io.*;

public class DeepCloneBase implements Serializable {
    public Object deepClone() {
        ByteArrayOutputStream byteArrayOutputStream = null;
        ObjectOutputStream objectOutputStream = null;
        ByteArrayInputStream byteArrayInputStream = null;
        ObjectInputStream objectInputStream = null;
        try {
            byteArrayOutputStream = new ByteArrayOutputStream();
            objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(this);
            byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            objectInputStream = new ObjectInputStream(byteArrayInputStream);
            return objectInputStream.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                byteArrayOutputStream.close();
                objectOutputStream.close();
                byteArrayInputStream.close();
                objectInputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

}
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;

@Data
@AllArgsConstructor
public class MyCar extends DeepCloneBase {

    private String brand;
    private double price;
    private CarProperty carProperty;

    public static void main(String[] args) throws  Exception{
        // 注意CarProperty也須要實現Serializable接口,代碼再也不單獨列出
        CarProperty carProperty = new CarProperty("8匹",250,30);
        MyCar car= new MyCar("BMW",200,carProperty);

        MyCar copy = (MyCar)car.deepClone();

        if (copy!=null){
            System.out.println("copy最大速度爲: "+copy.getCarProperty().getMaxSpeed());
            System.out.println("原型最大速度爲: "+car.getCarProperty().getMaxSpeed());
            car.getCarProperty().setMaxSpeed(360);
            System.out.println("copy最大速度爲: "+copy.getCarProperty().getMaxSpeed());
            System.out.println("原型最大速度爲: "+car.getCarProperty().getMaxSpeed());
        }else{
            System.out.println("對象沒拷貝成功....");
        }
    }
}

總結

優勢: 1. 若是對象建立比較複雜, 能夠簡化建立過程, 提升效率;2. 能夠保留對象狀態;
缺點: 對於clone()方式,若是類有修改則須要修改clone()的實現,不符合開閉原則; 複雜對象的clone邏輯可能較複雜;htm

JDK

java.lang.Object#clone()

相關文章
相關標籤/搜索