【23种设计模式专题】三 原型模式

程序猿学社的GitHub,欢迎Star
github技术专题
本文已记录到github

前言

通过前面两章,我们已经知道单例模式,工厂模式,本文就来看一看原型模式

概念

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式属于一种创建型的设计模式

  • 可以理解为克隆技术,克隆羊
  • copy技术,直接原封不动的copy一个内容
  • 修真小说,到了元婴期,就是肉体被毁灭,只要元婴没有被灭,还可以复活。
  • 王者荣耀里面最新出来的一个英雄,镜,可以制造一个分身。分身也拥有攻击力

实战

场景

隔壁老王: “社长,你们公司需要填写周报吗?”
社长: “需要填写的,而且每周,都会有一次周例会”
隔壁老王: “填写周报好浪费时间,还得排版,再填写内容”
社长: “你是怎么实现的”
隔壁老王: “我每次重新生成一个excel,再排版的”
社长: “老铁,你这太实诚咯”

传统方式

package com.cxyxs.designmode.prototype;

import lombok.Data;

import java.util.Date;

/**
 * Description:
 * Author: 程序猿学社
 * Date:  2020/4/12 18:42
 * Modified By:
 */
@Data
public class Weekly {
    private String name;
    /** 本周计划 */
    private String weekPlan;
    /** 下周计划 */
    private String nextWeekPlan;
    /**  填写时间 */
    private Date createTime;

    public Weekly(String name, String weekPlan, String nextWeekPlan, Date createTime) {
        this.name = name;
        this.weekPlan = weekPlan;
        this.nextWeekPlan = nextWeekPlan;
        this.createTime = createTime;
    }
}
  • 使用@Data注解,实际上就是简化代码,无需写set get方法,使用了lombok插件
package com.cxyxs.designmode.prototype;

import java.util.Date;

/**
 * Description:
 * Author: 程序猿学社
 * Date:  2020/4/12 18:45
 * Modified By:
 */
public class WeeklyTest {
    public static void main(String[] args) {
        Weekly weekly = new Weekly("隔壁小王","用户模块开发","订单模块开发",new Date());
        System.out.println("--------本周周报--------");
        System.out.println(weekly.toString());
        System.out.println("--------下周周报--------");
        Weekly weekly1 = new Weekly("隔壁小王","订单模块开发","订单模块开发相关单元测试",new Date());
        System.out.println(weekly1.toString());
    }
}


隔壁老王: “社长,有没有什么方法,可以简化周报这块,提高我编写周报的效率”
社长: “直接copy一份,上一次编写的周报(原型模式),在此之上,再做修改”

原型模式

package com.cxyxs.designmode.prototype.shallow;

import lombok.Data;

import java.util.Date;

/**
 * Description:
 * Author: 程序猿学社
 * Date:  2020/4/12 18:42
 * Modified By:
 */
@Data
public class Weekly implements  Cloneable{
    private String name;
    /** 本周计划 */
    private String weekPlan;
    /** 下周计划 */
    private String nextWeekPlan;
    /**  填写时间 */
    private Date createTime;

    public Weekly(String name, String weekPlan, String nextWeekPlan, Date createTime) {
        this.name = name;
        this.weekPlan = weekPlan;
        this.nextWeekPlan = nextWeekPlan;
        this.createTime = createTime;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

通过上面的代码,我们小小的梳理一下,实现的过程
实现原型模式就两步:

  • 实现Cloneable接口
  • 重写clone方法
  • 学过java的社友,都知道,Object是一切类的老祖宗,代码里面的super.clone(),实际上,就是调用Object的clone方法,通过查看他的源码,发现,又遇到native方法,说明实现克隆不是通过java实现的(c或者c++)
package com.cxyxs.designmode.prototype.shallow;


import java.util.Date;

/**
 * Description:
 * Author: 程序猿学社
 * Date:  2020/4/12 18:45
 * Modified By:
 */
public class WeeklyTest {
    public static void main(String[] args) throws  Exception{
        Date date = new Date();
        Weekly weekly = new Weekly("隔壁小王","用户模块开发","订单模块开发",date);
        System.out.println("--------本周周报--------");
        System.out.println(weekly.toString());
        System.out.println("--------下周周报--------");
        Weekly weekly1 = (Weekly) weekly.clone();    //使用克隆(copy方式)
        //Weekly weekly1 = new Weekly("隔壁小王","订单模块开发","订单模块开发相关单元测试",new Date());
        System.out.println(weekly1.toString());
        System.out.println(weekly == weekly1);
    }
}

  • 可以发现结果为false,说明重新生成了一个对象,而且克隆的对象和原来的一模一样,回顾一下,我们第一章学习过的单例模式,又增加了一种,可以破解单例的方法。
    单例模式,谁说程序猿没有女(男)朋友?

社长: “实际上,上面这种写法还存在一点的问题”
隔壁老王: “社长,存在什么问题”
社长: “上面这种写法,只能实现浅克隆,没有实现完全的克隆,我们先通过一个例子来看一看把”

浅克隆

package com.cxyxs.designmode.prototype.shallow;


import java.util.Date;

/**
 * Description:
 * Author: 程序猿学社
 * Date:  2020/4/12 18:45
 * Modified By:
 */
public class WeeklyTest1 {
    public static void main(String[] args) throws  Exception{
        Date date = new Date();

        Weekly weekly = new Weekly("隔壁小王","用户模块开发","订单模块开发",date);
        Weekly weekly1 = (Weekly) weekly.clone();    //使用克隆(copy方式)
        System.out.println(weekly.toString());
        System.out.println(weekly1.toString());
        //修改克隆后对象的值
        date.setTime(1546315871);
        System.out.println("--------------");
        System.out.println(weekly.toString());
        System.out.println(weekly1.toString());
    }
}

  • 先实现克隆后,我们再修改createTime的值,可以发现,修改后,克隆前和克隆后的值都一样,说明这两个对象的指向的都是同一个地方。

浅克隆

  • 浅克隆会进行值传递(只是针对基本数据类型)
  • 如果是引用数据类型,浅克隆会进行引用传递,实际上,就是把引用地址copy一份给新的对象,实际上,都是指向同一个地址。

深克隆

重写clone方法实现深克隆

package com.cxyxs.designmode.prototype.shallow;

import lombok.Data;

import java.util.Date;

/**
 * Description:
 * Author: 程序猿学社
 * Date:  2020/4/12 18:42
 * Modified By:
 */
@Data
public class Weekly implements  Cloneable{
    private String name;
    /** 本周计划 */
    private String weekPlan;
    /** 下周计划 */
    private String nextWeekPlan;
    /**  填写时间 */
    private Date createTime;

    public Weekly(String name, String weekPlan, String nextWeekPlan, Date createTime) {
        this.name = name;
        this.weekPlan = weekPlan;
        this.nextWeekPlan = nextWeekPlan;
        this.createTime = createTime;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        try {
            Weekly weekly = (Weekly) super.clone();
            //把数据克隆
            Date clone = (Date) createTime.clone();
            weekly.setCreateTime(clone);
            return weekly;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

  • 因为考虑到createTime,指向的是同一个引用,所以,我们每次克隆的时候,都把这个字段重新克隆一次。
  • 通过上图,可以看出,修改时间,只是克隆前的值发生了改变。 可以这样理解,在修真小说内,到了分神期,可以修炼第二分身,实际上,你攻击本体时,只是本体受到伤害,就算本体死亡,第二分身还可以继续存活。

隔壁老王: “社长,看了一下,你写的代码,这还是属性的,如果有上百个属性,我这一个个改,也忒麻烦咯。有没有不用这样一个个设置的方法”
社长: “这种方法的copy,实际上是内存的copy,直接操作内存的,也是性能最好的,如果你觉得这种方式太麻烦了,我们可以通过序列化和反序列的方式来实现”

通过序列化实现深克隆

package com.cxyxs.designmode.prototype.shallow.seri;

import lombok.Data;

import java.io.*;
import java.util.Date;

/**
 * Description:
 * Author: 程序猿学社
 * Date:  2020/4/12 18:42
 * Modified By:
 */
@Data
public class Weekly implements Serializable {
    private String name;
    /** 本周计划 */
    private String weekPlan;
    /** 下周计划 */
    private String nextWeekPlan;
    /**  填写时间 */
    private Date createTime;

    public Weekly(String name, String weekPlan, String nextWeekPlan, Date createTime) {
        this.name = name;
        this.weekPlan = weekPlan;
        this.nextWeekPlan = nextWeekPlan;
        this.createTime = createTime;
    }

    public Object copy() throws  Exception{
        //序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        //反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        Weekly weekly = (Weekly) ois.readObject();
        return weekly;
    }
}

实现序列化两部曲:

  • 实现Serializable接口
  • 实现序列化和反序列化对应的代码
package com.cxyxs.designmode.prototype.shallow.seri;



import java.util.Date;

/**
 * Description:
 * Author: 程序猿学社
 * Date:  2020/4/12 18:45
 * Modified By:
 */
public class WeeklyTest2 {
    public static void main(String[] args) throws  Exception{
        Date date = new Date();

        Weekly weekly = new Weekly("隔壁老王","用户模块开发","订单模块开发",date);
        Weekly weekly1 = (Weekly) weekly.copy();    //使用克隆(copy方式)
        System.out.println(weekly.toString());
        System.out.println(weekly1.toString());
        //修改克隆后对象的值
        date.setTime(11);
        System.out.println("--------------");
        System.out.println(weekly.toString());
        System.out.println(weekly1.toString());
    }
}
  • 通过这种方式实现对象的深度克隆,无需一个个设置引用类型属性的clone方法。

总结: 如果,需要创建大量的对象,建议使用原型模式。实际上他是不符合ocp原则的。应该实现对扩展开放,对修改关闭。不会调用类的重构方法 ,可以破解单例模式。


原创不易,不要白嫖,觉得有用的社友,给我点赞,让更多的老铁看到这篇文章。
因技术能力有限,如文中有不合理的地方,希望各位大佬指出,在下方评论留言,谢谢,希望大家一起进步,一起成长。

作者:程序猿学社
原创公众号:『程序猿学社』,专注于java技术栈,分享java各个技术系列专题,以及各个技术点的面试题。
原创不易,转载请注明来源(注明:来源于公众号:程序猿学社, 作者:程序猿学社)。

发布了292 篇原创文章 · 获赞 1901 · 访问量 47万+

猜你喜欢

转载自blog.csdn.net/qq_16855077/article/details/105458408