浅谈设计模式-原型模式

书接上回,本篇讲一下创建型模式-原型设计模式

原型设计模式

定义:用原型实例指定创建对象种类,并通过拷贝这些原型来创建新的对象。

另外在拷贝时,不需要知道任何构建过程细节。也不用调用构造器

原型设计模式本质:对象克隆

案例分析

需求:给10个用户发节日祝福短信

普通版

短信类

//短信
public class SMS {
    private String usename; //用户名
    private String phone;   //手机
    private String content;  //短信内容

    //通过短信模板拼接短信
    public String getContent(){
        return MessageFormat.format(SMSUtil.getSmsTemplate(), usename, content);
    }

    public String getUsename() {
        return usename;
    }

    public void setUsename(String usename) {
        this.usename = usename;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setContent(String content) {
        this.content = content;
    }

短信工具类

//短信工具类
public class SMSUtil {

    //发短信
    public static  void sendSms(SMS sms){
        System.out.println(MessageFormat.format("向手机号为:{0}的用户,发送了短信,内容为:{1}",
                sms.getPhone(), sms.getContent()));
    }

    //获取短信模板,用于拼接短信
    public static String getSmsTemplate(){
        return "亲爱的{0}女士/男士:{1}";
    }

}

测试

public class App {
    public static void main(String[] args) {
        for (int i = 10; i < 20; i++){
            SMS sms = new SMS();
            sms.setUsename("dafei_" + i);
            sms.setPhone("137000000" + i);
            sms.setContent("春节快乐");
            SMSUtil.sendSms(sms);
        }
    }
}

结果 

向手机号为:13700000010的用户,发送了短信,内容为:亲爱的dafei_10女士/男士:春节快乐
向手机号为:13700000011的用户,发送了短信,内容为:亲爱的dafei_11女士/男士:春节快乐
向手机号为:13700000012的用户,发送了短信,内容为:亲爱的dafei_12女士/男士:春节快乐
向手机号为:13700000013的用户,发送了短信,内容为:亲爱的dafei_13女士/男士:春节快乐
向手机号为:13700000014的用户,发送了短信,内容为:亲爱的dafei_14女士/男士:春节快乐
向手机号为:13700000015的用户,发送了短信,内容为:亲爱的dafei_15女士/男士:春节快乐
向手机号为:13700000016的用户,发送了短信,内容为:亲爱的dafei_16女士/男士:春节快乐
向手机号为:13700000017的用户,发送了短信,内容为:亲爱的dafei_17女士/男士:春节快乐
向手机号为:13700000018的用户,发送了短信,内容为:亲爱的dafei_18女士/男士:春节快乐
向手机号为:13700000019的用户,发送了短信,内容为:亲爱的dafei_19女士/男士:春节快乐

 原型版

短信类

有个2个变化,实现了Cloneable接口, 重写Object类的clone方法

//短信
public class SMS implements Cloneable{
    private String usename; //用户名
    private String phone;   //手机
    private String content;  //短信内容

    public String getContent(){
        return MessageFormat.format(SMSUtil.getSmsTemplate(), usename, content);
    }

    public String getUsename() {
        return usename;
    }

    public void setUsename(String usename) {
        this.usename = usename;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setContent(String content) {
        this.content = content;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object clone = super.clone();
        System.out.println("克隆了,原hashCode:" + this.hashCode() +" 克隆后hashCode:" + clone.hashCode());
        return clone;
    }
}

测试

public class App {
    public static void main(String[] args) throws CloneNotSupportedException {
        SMS sms = new SMS();
        sms.setContent("春节快乐");
        for (int i = 10; i < 20; i++){
            SMS clone = (SMS) sms.clone();
            clone.setUsename("dafei_" + i);
            clone.setPhone("137000000" + i);
            SMSUtil.sendSms(clone);
        }
    }
}

结果

克隆了,原hashCode:403424356 克隆后hashCode:321142942
向手机号为:13700000010的用户,发送了短信,内容为:亲爱的dafei_10女士/男士:春节快乐
克隆了,原hashCode:403424356 克隆后hashCode:1307096070
向手机号为:13700000011的用户,发送了短信,内容为:亲爱的dafei_11女士/男士:春节快乐
克隆了,原hashCode:403424356 克隆后hashCode:1014328909
向手机号为:13700000012的用户,发送了短信,内容为:亲爱的dafei_12女士/男士:春节快乐
克隆了,原hashCode:403424356 克隆后hashCode:2081303229
向手机号为:13700000013的用户,发送了短信,内容为:亲爱的dafei_13女士/男士:春节快乐
克隆了,原hashCode:403424356 克隆后hashCode:1223685984
向手机号为:13700000014的用户,发送了短信,内容为:亲爱的dafei_14女士/男士:春节快乐
克隆了,原hashCode:403424356 克隆后hashCode:1076835071
向手机号为:13700000015的用户,发送了短信,内容为:亲爱的dafei_15女士/男士:春节快乐
克隆了,原hashCode:403424356 克隆后hashCode:1463757745
向手机号为:13700000016的用户,发送了短信,内容为:亲爱的dafei_16女士/男士:春节快乐
克隆了,原hashCode:403424356 克隆后hashCode:1525262377
向手机号为:13700000017的用户,发送了短信,内容为:亲爱的dafei_17女士/男士:春节快乐
克隆了,原hashCode:403424356 克隆后hashCode:1837760739
向手机号为:13700000018的用户,发送了短信,内容为:亲爱的dafei_18女士/男士:春节快乐
克隆了,原hashCode:403424356 克隆后hashCode:1418428263
向手机号为:13700000019的用户,发送了短信,内容为:亲爱的dafei_19女士/男士:春节快乐

 从上面打印出来的hashcode值来看,发现每一个clone出来的对象都不一样,这就是克隆,原型模式差不多就这样啦。

适用场景

1>类初始化消耗较多资源
2>new产生的一个对象需要非常繁琐的过程(数据准备, 访问权限等)
3>构造函数比较复杂
4>需要重复生产大量相同或类似对象时

优缺点

优点
性能比直接new一个对象性能高(直接内存开辟空间)
简化构建过程(直接ctrl+c/v)

缺点
必须配备克隆方法
对克隆复杂对象或者克隆出的对象进行复杂改造时,容易引入风险(浅克隆风险)
深拷贝,浅拷贝要运用得当(克隆的前提)

克隆隐患

原型模式的安全隐藏在克隆方式,java中的克隆有2种:浅克隆跟深克隆

浅克隆

看一个例子

部门类

public class Dept {
    private String name;
    public Dept(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Dept{name='" + name +"', hashcode="+this.hashCode()+"}";
    }
}

员工类

员工类依赖Dept部门类,同时实现了Cloneable接口,重写clone方法

public class Emp implements  Cloneable{
    private String name;
    private Dept dept;

    public Emp(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Dept getDept() {
        return dept;
    }
    public void setDept(Dept dept) {
        this.dept = dept;
    }
    @Override
    public String toString() {
        return "Emp{" +
                "name='" + name + '\'' + ", hashcode:" + this.hashCode() +
                ", dept=" + dept +
                '}';
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

测试

创建一个部门类,员工类,将部门对象赋值给员工类部门属性,执行克隆之后修改部门对象name属性

public class App {
    public static void main(String[] args) throws CloneNotSupportedException {

        //克隆前
        Dept dept = new Dept("开发部");
        Emp emp = new Emp("dafei");
        emp.setDept(dept);
        System.out.println(emp);
        System.out.println("------------------------");
        //克隆后
        Emp clone  = (Emp) emp.clone();
        dept.setName("测试部");
        System.out.println(clone);
    }
}

结果

Emp{name='dafei', hashcode:321142942, dept=Dept{name='开发部', hashcode=1223685984}}
------------------------
Emp{name='dafei', hashcode:1374677625, dept=Dept{name='测试部', hashcode=1223685984}}

从结果打印出来的hashcode值来看, 员工对象确实实现拷贝,已经是2个不同对象了,但是部门并不是2个对象。也就是说员工类的super.clone()方法仅仅是对员工对象克隆而已,员工对象的对象属性并没有进行拷贝,这种现象称之为浅拷贝。

上面描述换成图形

 浅克隆的危险性在,引用类型的属性变动会给原型对象造成属性变动隐患。那怎么改进?使用深克隆。引用类型的属性也进行克隆。

深克隆

部门类

让部门类也实现Cloneable接口,实现clone方法

public class Dept implements Cloneable{
    private String name;
    public Dept(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Dept{name='" + name +"', hashcode="+this.hashCode()+"}";
    }

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

员工类

修改了clone方法

public class Emp implements  Cloneable{
    private String name;
    private Dept dept;

    public Emp(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Dept getDept() {
        return dept;
    }
    public void setDept(Dept dept) {
        this.dept = dept;
    }
    @Override
    public String toString() {
        return "Emp{" +
                "name='" + name + '\'' + ", hashcode:" + this.hashCode() +
                ", dept=" + dept +
                '}';
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Emp emp = (Emp) super.clone();
        Dept dept  = (Dept) emp.getDept().clone();
        emp.setDept(dept);
        return emp;
    }
}

测试

没有变

结果

Emp{name='dafei', hashcode:321142942, dept=Dept{name='开发部', hashcode=1223685984}}
------------------------
Emp{name='dafei', hashcode:1374677625, dept=Dept{name='开发部', hashcode=1345636186}}

结果就很明显了,emp跟dept对象都完整克隆出来了。

开发案例

开发案例还是选用JDK里-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);
        }
    }

}

这里需要注意:ArrayList中元素使用Arrays.copyOf方法进行克隆,该方法时浅克隆。

总结

原型设计模式本质是:对象克隆

克隆时要注意:原型克隆时深克隆还是浅克隆

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

浪飞yes

我对钱没兴趣~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值