原型模式Prototype Pattern

185 阅读4分钟

学习目标

  • 掌握原型模式和建造者的应用场景
  • 掌握原型模式的浅克隆和深克隆的写法
  • 掌握建造者模式的基本写法
  • 了解克隆是如何破坏单例的
  • 了解原型模式的优缺点
  • 掌握建造者模式和工厂模式的区别

原型模式的定义

  • 原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创新的对象。
  • 调用者不需要知道任何创建细节,不调用构造函数
  • 属于创建型模式

原型模式的适用场景

  • 类初始化消耗资源较多
  • new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
  • 构造函数比较复杂
  • 循环体中生产大量对象时

原型模式Java JDK中的应用

  • ArrayList
  • HashMap

原型模式的优点

  • 性能优良,Java自带的原型模式是基于内存二进制流的拷贝,比直接new一个对象性能上提升了许多
  • 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,简化了创建过程,不需要通过构造方法或者get\set方法反复设置参数

原型模式的缺点

  • 必须配备克隆(或者可拷贝)方法
  • 当对已有类进行改造的时候,需要修改代码,违反了开闭原则
  • 深拷贝、浅拷贝需要运用得当
  • 原型模式和单例模式需求区分开使用(一个类不能是单例又是原型模式)

深克隆和浅克隆的区别

  • 深克隆拷贝的是值
  • 浅克隆拷贝的是引用

原型

  • 例子 原型对象
import lombok.Data;

@Data
public class ExamPaper{

   private String examinationPaperId;//试卷主键
   private String leavTime;//剩余时间
   private String organizationId;//单位主键
   private String id;//考试主键
   private String examRoomId;//考场主键
   private String userId;//用户主键
   private String specialtyCode;//专业代码
   private String postionCode;//报考岗位
   private String gradeCode;//报考等级
   private String examStartTime;//考试开始时间
   private String examEndTime;//考试结束时间
   private String singleSelectionImpCount;//单选选题重要数量
   private String multiSelectionImpCount;//多选题重要数量
   private String judgementImpCount;//判断题重要数量
   private String examTime;//考试时长
   private String fullScore;//总分
   private String passScore;//及格分
   private String userName;//学员姓名
   private String score;//考试得分
   private String resut;//是否及格
   private String singleOkCount;//单选题答对数量
   private String multiOkCount;//多选题答对数量
   private String judgementOkCount;//判断题答对数量

   public ExamPaper copy(){
      ExamPaper examPaper = new ExamPaper();
      //剩余时间
      examPaper.setLeavTime(this.getLeavTime());
      //单位主键
      examPaper.setOrganizationId(this.getOrganizationId());
      //考试主键
      examPaper.setId(this.getId());
      //用户主键
      examPaper.setUserId(this.getUserId());
      //专业
      examPaper.setSpecialtyCode(this.getSpecialtyCode());
      //岗位
      examPaper.setPostionCode(this.getPostionCode());
      //等级
      examPaper.setGradeCode(this.getGradeCode());
      //考试开始时间
      examPaper.setExamStartTime(this.getExamStartTime());
      //考试结束时间
      examPaper.setExamEndTime(this.getExamEndTime());
      //单选题重要数量
      examPaper.setSingleSelectionImpCount(this.getSingleSelectionImpCount());
      //多选题重要数量
      examPaper.setMultiSelectionImpCount(this.getMultiSelectionImpCount());
      //判断题重要数量
      examPaper.setJudgementImpCount(this.getJudgementImpCount());
      //考试时间
      examPaper.setExamTime(this.getExamTime());
      //总分
      examPaper.setFullScore(this.getFullScore());
      //及格分
      examPaper.setPassScore(this.getPassScore());
      //学员姓名
      examPaper.setUserName(this.getUserName());
      //分数
      examPaper.setScore(this.getScore());

      //单选答对数量
      examPaper.setSingleOkCount(this.getSingleOkCount());
      //多选答对数量
      examPaper.setMultiOkCount(this.getMultiOkCount());
      //判断答对数量
      examPaper.setJudgementOkCount(this.getJudgementOkCount());

      return examPaper;
   }

   @Override
   public String toString() {
      return "ExamPaper{" +
              "examinationPaperId='" + examinationPaperId + '\'' +
              ", leavTime='" + leavTime + '\'' +
              ", organizationId='" + organizationId + '\'' +
              ", id='" + id + '\'' +
              ", examRoomId='" + examRoomId + '\'' +
              ", userId='" + userId + '\'' +
              ", specialtyCode='" + specialtyCode + '\'' +
              ", postionCode='" + postionCode + '\'' +
              ", gradeCode='" + gradeCode + '\'' +
              ", examStartTime='" + examStartTime + '\'' +
              ", examEndTime='" + examEndTime + '\'' +
              ", singleSelectionImpCount='" + singleSelectionImpCount + '\'' +
              ", multiSelectionImpCount='" + multiSelectionImpCount + '\'' +
              ", judgementImpCount='" + judgementImpCount + '\'' +
              ", examTime='" + examTime + '\'' +
              ", fullScore='" + fullScore + '\'' +
              ", passScore='" + passScore + '\'' +
              ", userName='" + userName + '\'' +
              ", score='" + score + '\'' +
              ", resut='" + resut + '\'' +
              ", singleOkCount='" + singleOkCount + '\'' +
              ", multiOkCount='" + multiOkCount + '\'' +
              ", judgementOkCount='" + judgementOkCount + '\'' +
              '}';
   }
}

BeanUtils拷贝对象,因为属性很多,用反射赋值(代码简洁,代码量少)

import java.lang.reflect.Field;
public class BeanUtils {
    public static Object copy(Object protorype) {
        Class clazz = protorype.getClass();
        Object returnValue = null;
        try {
            returnValue = clazz.newInstance();
            for (Field field : clazz.getDeclaredFields()) {
                field.setAccessible(true);
                field.set(returnValue, field.get(protorype));
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return returnValue;
    }
}

客户端测试

public class Client {
    public static void main(String[] args) {

//        ExamPaper examPaper = new ExamPaper();
//        System.out.println(examPaper);

        ExamPaper examPaper = (ExamPaper)BeanUtils.copy(new ExamPaper());
        System.out.println(examPaper);
    }
}

一、浅克隆

  • 结论:hobbies是引用数据类型,浅克隆,拷贝的是原型对象值,他们是同一个引用,并没有生成新的对象
@Data
public class ConcretePrototype implements Cloneable {
    private int age;
    private String name;
    private List<String> hobbies;
    @Override
    public ConcretePrototype clone() {
        try {
            return (ConcretePrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
    @Override
    public String toString() {
        return "ConcretePrototype{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", hobbies=" + hobbies +
                '}';
    }
}
public class Client {

    public static void main(String[] args) {
        // 创建原型对象
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setAge(18);
        prototype.setName("Lisa");
        List<String> hobbies = new ArrayList<>();
        hobbies.add("音乐");
        hobbies.add("美术");
        prototype.setHobbies(hobbies);


        // 拷贝原型对象
        ConcretePrototype cloneType = prototype.clone();
        cloneType.getHobbies().add("篮球");
        System.out.println("原型对象:" + prototype);
        System.out.println("克隆对象:" + cloneType);
        System.out.println(prototype == cloneType);

        System.out.println("原型对象的爱好:" + prototype.getHobbies());
        System.out.println("克隆对象的爱好:" + cloneType.getHobbies());
        System.out.println(prototype.getHobbies() == cloneType.getHobbies());

    }
}

image.png

二、深克隆

@Data
public class ConcretePrototype implements Cloneable,Serializable {

    private int age;
    private String name;
    private List<String> hobbies;

    @Override
    public ConcretePrototype clone() {
        try {
            return (ConcretePrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    public ConcretePrototype deepClone(){
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);

            return (ConcretePrototype) ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    @Override
    public String toString() {
        return "ConcretePrototype{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", hobbies=" + hobbies +
                '}';
    }
}
import java.util.ArrayList;
import java.util.List;

public class Client {

    public static void main(String[] args) {
        // 创建原型对象
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setAge(18);
        prototype.setName("Lisa");
        List<String> hobbies = new ArrayList<>();
        hobbies.add("音乐");
        hobbies.add("美术");
        prototype.setHobbies(hobbies);


        // 拷贝原型对象
        ConcretePrototype cloneType = prototype.deepClone();
        cloneType.getHobbies().add("篮球");
        System.out.println("原型对象:" + prototype);
        System.out.println("克隆对象:" + cloneType);
        System.out.println(prototype == cloneType);

        System.out.println("原型对象的爱好:" + prototype.getHobbies());
        System.out.println("克隆对象的爱好:" + cloneType.getHobbies());
        System.out.println(prototype.getHobbies() == cloneType.getHobbies());

    }
}

执行结果: image.png

单例


import lombok.Data;

import java.util.List;

@Data
public class ConcretePrototype implements Cloneable {

    private int age;
    private String name;
    private List<String> hobbies;

    private static ConcretePrototype instance = new ConcretePrototype();
    private ConcretePrototype(){}
    public static ConcretePrototype getInstance(){
        return instance;
    }

    @Override
    public ConcretePrototype clone() {
        try {
            return (ConcretePrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public String toString() {
        return "ConcretePrototype{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", hobbies=" + hobbies +
                '}';
    }
}
import java.util.ArrayList;
import java.util.List;
public class Client {
    public static void main(String[] args) {
        // 创建原型对象
        ConcretePrototype prototype = ConcretePrototype.getInstance();
        prototype.setAge(18);
        prototype.setName("Lisa");
        List<String> hobbies = new ArrayList<>();
        hobbies.add("音乐");
        hobbies.add("美术");
        prototype.setHobbies(hobbies);


        // 拷贝原型对象
        ConcretePrototype cloneType = prototype.clone();
        cloneType.getHobbies().add("篮球");
        System.out.println("原型对象:" + prototype);
        System.out.println("克隆对象:" + cloneType);
        System.out.println(prototype == cloneType);

        System.out.println("原型对象的爱好:" + prototype.getHobbies());
        System.out.println("克隆对象的爱好:" + cloneType.getHobbies());
        System.out.println(prototype.getHobbies() == cloneType.getHobbies());

    }
}

image.png

  • 如果是单例模式,那么就不要实现Cloneable接口
  • 如果要实现,clone方法如下
@Override
public ConcretePrototype clone() {
    return instance;
}
  • 原型跟单例模式是冲突的,只能二选一