23种设计模式之原型模式

优点:主要用于对象复制,可克隆出来再做赋值修改以达到对象满足,减少new对象的性能损耗,克隆类似于new,但跟new不同,new是所有属性都是默认值,而克隆是带原型对象中的值。
缺点:逃避构造函数的约束。

分三部分:
1.浅复制
2.深复制
3.序列化反序列化实现深复制

以保时捷为例子,创建一台车,然后复制一台车,想要做到复制,只需在类中做两件事:
1.需要实现Cloneable 接口
2.继承Object的clone方法

浅复制
import java.util.Date;

/**
 * 这是一个保时捷实体类
 * porscheModel 保时捷型号
 * interior 内饰
 * wheelHub 轮毂规格
 * date 生产时间
 */

public class Porsche implements Cloneable {

    private String porscheModel;
    private String interior;
    private int wheelHub;
    private Date date;

    public Porsche(){

    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public String getPorscheModel() {
        return porscheModel;
    }

    public void setPorscheModel(String porscheModel) {
        this.porscheModel = porscheModel;
    }

    public String getInterior() {
        return interior;
    }

    public void setInterior(String interior) {
        this.interior = interior;
    }

    public int getWheelHub() {
        return wheelHub;
    }

    public void setWheelHub(int wheelHub) {
        this.wheelHub = wheelHub;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
}

测试:

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.util.Log;

import com.hdc.test.prototype.Porsche;

import java.util.Date;

/**
 * 这是调用者
 */

public class MainActivity extends FragmentActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Date date = new Date(351213211);
        Porsche porsche = new Porsche();
        porsche.setPorscheModel("保时捷911");
        porsche.setInterior("石榴红真皮内饰");
        porsche.setWheelHub(20);
        porsche.setDate(date);

        try {
            //浅复制
            Porsche porsche2 = (Porsche) porsche.clone();
            //打印
            Log.d("TAG", "原型对象:" + porsche + "\n克隆对象:" + porsche2);
            Log.d("TAG", "原型对象date的值:" + porsche.getDate() + "\n克隆对象date的值:" + porsche2.getDate());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

结果:


23种设计模式之原型模式_第1张图片
image.png

克隆出了一个全新对象,值也都是一样的,引用地址也是一样的,但是浅克隆会有一个问题,就是当你把Date对象修改之后,克隆对象的值也会跟着改变。
测试:

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.util.Log;

import com.hdc.test.prototype.Porsche;

import java.util.Date;

/**
 * 这是调用者
 */

public class MainActivity extends FragmentActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Date date = new Date(351213211);
        Porsche porsche = new Porsche();
        porsche.setPorscheModel("保时捷911");
        porsche.setInterior("石榴红真皮内饰");
        porsche.setWheelHub(20);
        porsche.setDate(date);
        Log.d("TAG", "修改前原型对象date的值:" + porsche.getDate());

        try {
            //浅复制
            Porsche porsche2 = (Porsche) porsche.clone();
            //克隆完成后修改date的值
            date.setTime(325623262);

            //打印
            Log.d("TAG", "原型对象date的值:" + porsche.getDate() + "\n克隆对象date的值:" + porsche2.getDate());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

结果:


23种设计模式之原型模式_第2张图片
image.png

克隆对象porsche2 时间也随之改变了,我在date修改前已经复制出来了,为什么还是会改变?因为porsche和porsche2 引用的是同一个对象,他们获取值的引用地址是一样的,当引用地址的值被改变之后,他们也随之改变,因此要避免这种情况需要用到深复制。

深复制

很简单,Porsche 实体类中的clone()方法做以下重写,把属性也进行克隆就好

 @Override
    public Object clone() throws CloneNotSupportedException {
        Object obj = super.clone();
        Porsche porsche = (Porsche) obj;
        porsche.date = (Date) this.date.clone();
        return obj;
    }

结果:
image.png

这样就能保证,当引用地址值发生改变时,克隆对象中的值不会发生改变。

序列化与反序列化实现深复制

序列化之前实体类需要实现Serializable,clone方法返回父类的clone方法就行了,不需要重写。

import java.io.Serializable;
import java.util.Date;

/**
 * 浅复制
 * 这是一个保时捷实体类
 * porscheModel 保时捷型号
 * interior 内饰
 * wheelHub 轮毂规格
 * date 生产时间
 */

public class Porsche implements Cloneable, Serializable {

    private String porscheModel;
    private String interior;
    private int wheelHub;
    private Date date;

    public Porsche() {

    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public String getPorscheModel() {
        return porscheModel;
    }

    public void setPorscheModel(String porscheModel) {
        this.porscheModel = porscheModel;
    }

    public String getInterior() {
        return interior;
    }

    public void setInterior(String interior) {
        this.interior = interior;
    }

    public int getWheelHub() {
        return wheelHub;
    }

    public void setWheelHub(int wheelHub) {
        this.wheelHub = wheelHub;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
}

测试:

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.util.Log;

import com.hdc.test.prototype.Porsche;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;

/**
 * 这是调用者
 */

public class MainActivity extends FragmentActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Date date = new Date(351213211);
        Porsche porsche = new Porsche();
        porsche.setPorscheModel("保时捷911");
        porsche.setInterior("石榴红真皮内饰");
        porsche.setWheelHub(20);
        porsche.setDate(date);
        Log.d("TAG", "修改前原型对象date的值:" + porsche.getDate());

        try {
            //序列化和反序列化实现深复制
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(porsche);
            byte[] bytes = bos.toByteArray();

            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bis);
            Porsche porsche2 = (Porsche) ois.readObject();

            //克隆完成后修改date的值
            date.setTime(325623262);

            //打印
            Log.d("TAG", "原型对象date的值:" + porsche.getDate() + "\n克隆对象date的值:" + porsche2.getDate());
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

结果:


image.png

引用地址的值发生改变,克隆对象数据并未改变,这样一样可以实现深复制。

最后以new对象以及克隆对象的耗时测试作为总结:
模拟对象耗时操作:

/**
 * 保时捷911实体类
 * 模拟对象耗时操作,假设创建一个对象需要5秒
 */

public class Porsche911 implements Cloneable {

    public Porsche911() {
        try {
            Thread.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.util.Log;

import com.hdc.test.prototype.Porsche911;

/**
 * 这是调用者
 */

public class MainActivity extends FragmentActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //都创建1000个对象
        testNew(1000);
        testClone(1000);
    }

    //new的方式构建对象
    private void testNew(int size) {
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < size; i++) {
            Porsche911 porsche911 = new Porsche911();
        }
        long endTime = System.currentTimeMillis();
        Log.d("TAG", "New方式创建对象耗时:" + (endTime - startTime));
    }

    private void testClone(int size) {
        long startTime = System.currentTimeMillis();
        Porsche911 porsche911 = new Porsche911();
        for (int i = 0; i < size; i++) {
            try {
                Porsche911 porsche9112 = (Porsche911) porsche911.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
        }
        long endTime = System.currentTimeMillis();
        Log.d("TAG", "clone方式创建对象耗时:" + (endTime - startTime));
    }
}

结果:


image.png

如果是很耗时的对象的话,效率差的不是一星半点。
已完成测试!有不对的地方欢迎指出,感恩。

你可能感兴趣的:(23种设计模式之原型模式)