厚积薄发打卡Day32 :[kuangStudy] GoF23通俗易懂的设计模式之 <原型模式>

前言:

视频教程:狂神说Java之通俗易懂的23种设计模式

什么是设计模式?

  • 设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。
    它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的
    解决方案==> 一种思维,一种态度,一种进步
  • 1995年,GoF(Gang of Four,四人组/四人帮)合作出版了《设计模式:可复用面向对象软
    件的基础》一书,共收录了23种设计模式,从此树立了软件设计模式领域的里程碑,人称 【GoF设计模式】
设计模式分类具体模式
创建型模式:
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
⌛单例模式、⌛工厂模式、⌛抽象工厂模式、⌛建造者模式、⌛原型模式
结构型模式:
这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。
适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
行为型模式:
这些设计模式特别关注对象之间的通信。
模板方法模、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式

原型模式:

原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

说人话,原型模式就是Java官方的cv方法(又称 克隆模式),其中要学会区别 深克隆浅克隆 的区别(也称作“浅拷贝”和“深拷贝”)

扩展阅读:

  1. 用故事讲述克隆模式:原型模式
  2. 图文结合: 深度好文:设计模式之——原型模式

代码:

实现步骤:

  1. 实现Cloneable接口

  2. 重写clone()方法,

    只有当一个类实现了Cloneable接口后,该类才会被赋予调用重写自Object类的clone方法得权利。否则会抛出“CloneNotSupportedException”异常。

举例bilibili视频

  • 浅克隆:

    1. 当类的成员变量是基本数据类型时,浅拷贝会复制该属性的值赋值给新对象。
    2. 当成员变量是引用数据类型时,浅拷贝复制的是引用数据类型的地址值。这种情况下,当拷贝出的某一个类修改了引用数据类型的成员变量后,会导致所有拷贝出的类都发生改变。

    默认实现父类的克隆方法都是浅克隆

    Vlog对象

public class Vlog implements Cloneable {

    private String name;
    private Date createTime;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    //省略对属性的get/set方法,与toString方法
}

客户端:

public class Bilibili {
    public static void main(String[] args) throws CloneNotSupportedException {
        //原型对象:
        Date date = new Date();
        Vlog v1 = new Vlog("Wayne的Vlog", date);
        //v1克隆出v2
        //Vlog v2 = new Vlog("Wayne的Vlog", date);
        Vlog v2 = (Vlog) v1.clone();
        System.out.println("v1==>"+v1);
        System.out.println("v1=>hash==>"+v1.hashCode());
        System.out.println("v2==>"+v2);
        System.out.println("v2=>hash==>"+v2.hashCode());
        //此时v1与v2属性是一样的
        //由于hashCode不同,可以对值进行单独修改
        v2.setName("盗版Vlog");
        System.out.println("v2==>"+v2);

        //但这时候将date对象属性改变
        System.out.println("=====================改变时间=====================");
        date.setTime(66666666L);
        System.out.println("v1==>"+v1);
        System.out.println("v2==>"+v2);
        //这时候v1、v2两个实例的date属性将会一起改变
    }
}
v1==>Vlog{name='Wayne的Vlog', createTime=Mon Mar 08 21:04:35 CST 2021}
v1=>hash==>1580066828
v2==>Vlog{name='Wayne的Vlog', createTime=Mon Mar 08 21:04:35 CST 2021}
v2=>hash==>491044090
v2==>Vlog{name='盗版Vlog', createTime=Mon Mar 08 21:04:35 CST 2021}
=====================改变时间=====================
v1==>Vlog{name='Wayne的Vlog', createTime=Fri Jan 02 02:31:06 CST 1970}
v2==>Vlog{name='盗版Vlog', createTime=Fri Jan 02 02:31:06 CST 1970}
  • 深克隆

    1. 深拷贝不仅会复制成员变量为基本数据类型的值,给新对象。
    2. 还会给是引用数据类型的成员变量申请储存空间,并复制引用数据类型成员变量的对象。这样拷贝出的新对象就不怕修改了是引用数据类型的成员变量后,对其它拷贝出的对象造成影响了.
public class Vlog implements Cloneable {
    private String name;
    private Date createTime;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Vlog vlog = (Vlog) super.clone();
        vlog.setCreateTime((Date) this.getCreateTime().clone());
        //或者直接给属性赋值也行:
        //vlog.createTime = (Date) this.createTime.clone();
        return vlog;
    }
    //省略对属性的get/set方法,与toString方法
}

这时再去客户端测试:

public class Bilibili {
    public static void main(String[] args) throws CloneNotSupportedException {
        //原型对象:
        Date date = new Date();
        Vlog v1 = new Vlog("Wayne的Vlog", date);
        //v1克隆出v2
        //Vlog v2 = new Vlog("Wayne的Vlog", date);
        Vlog v2 = (Vlog) v1.clone();
        v2.setName("盗版的Vlog");
        System.out.println("v1==>"+v1);
        System.out.println("v2==>"+v2);

        //但这时候将date对象属性改变
        System.out.println("=====================改变时间=====================");
        date.setTime(66666666L);
        System.out.println("v1==>"+v1);
        System.out.println("v2==>"+v2);
    }
}
v1==>Vlog{name='Wayne的Vlog', createTime=Mon Mar 08 21:24:19 CST 2021}
v2==>Vlog{name='盗版的Vlog', createTime=Mon Mar 08 21:24:19 CST 2021}
=====================改变时间=====================
v1==>Vlog{name='Wayne的Vlog', createTime=Fri Jan 02 02:31:06 CST 1970}
v2==>Vlog{name='盗版的Vlog', createTime=Mon Mar 08 21:24:19 CST 2021}

深/浅克隆-小结:

浅拷贝:

  1. 当类的成员变量是基本数据类型时,浅拷贝会复制该属性的值赋值给新对象。
  2. 当成员变量是引用数据类型时,浅拷贝复制的是引用数据类型的地址值。这种情况下,当拷贝出的某一个类修改了引用数据类型的成员变量后,会导致所有拷贝出的类都发生改变。

深拷贝:

  1. 深拷贝不仅会复制成员变量为基本数据类型的值,给新对象。

  2. 还会给是引用数据类型的成员变量申请储存空间,并复制引用数据类型成员变量的对象。这样拷贝出的新对象就不怕修改了是引用数据类型的成员变量后,对其它拷贝出的对象造成影响了。

  3. Java如何对一个对象进行深拷贝?

    在这里插入图片描述

模式优缺点分析

原型模式的优点:
  • Java自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加高。
    //源码直接调用native 方法
    protected native Object clone() throws CloneNotSupportedException;
    
  • 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),实现在Java层面的svn操作
原型模式的缺点:
  • 需要为每一个类都配置一个 clone 方法
  • clone 方法位于类的内部,当对已有类进行改造的时候,需要修改其中的clone代码,违背了开闭原则。
  • 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
  • 克隆破坏单例
    单例模式中,只要将构造方法的访问权限设置为private型,就可以实现单例。但是clone方法直接无视构造方法的权限(同上,直接调用native方法),所以,单例模式与原型模式是冲突的,在使用时要特别注意。其实防御方式很简单,单例类不要实现Cloneable接口即可。

应用场景:

  1. ArrayList中clone()方法的源码

    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);
        }
    }
    
  2. Spring中的原型模式:

    在Spring中,用户也可以采用原型模式来创建新的Bean实例,从而实现每次获取的是通过克隆生成的新实例,对其进行修改时对原有实例对象不造成任何影响。
    在每次使用对象之前,都会创建一个新的对象,并且会将依赖关系完整的赋值给这个新创建的对象。这样有利于节省系统资源,还可以更好的对原型管理器对象进行控制。

Spring中Bean的作用域、生命周期

在这里插入图片描述

  1. singleton:单例模式,Spring IoC容器中只会存在一个共享的Bean实例,无论有多少个Bean引用它,始终指向同一对象。Singleton作用域是Spring中的缺省作用域,也可以显示的将Bean定义为singleton模式,配置为:

    <bean id="userDao" class="com.ioc.UserDaoImpl" scope="singleton"/>
    

  2. prototype:原型模式,每次通过Spring容器获取prototype定义的bean时,容器都将创建一个新的Bean实例,每个Bean实例都有自己的属性和状态,而singleton全局只有一个对象。

    根据经验,对有状态的bean使用prototype作用域,而对无状态的bean使用singleton作用域。


  3. request:在一次Http请求中,容器会返回该Bean的同一实例。而对不同的Http请求则会产生新的Bean,而且该bean仅在当前Http Request内有效。

    <bean id="loginAction" class="com.cnblogs.Login" scope="request"/>
    

    针对每一次Http请求,Spring容器根据该bean的定义创建一个全新的实例,且该实例仅在当前Http请求内有效,而其它请求无法看到当前请求中状态的变化,当当前Http请求结束,该bean实例也将会被销毁。

  4. session:在一次Http Session中,容器会返回该Bean的同一实例。而对不同的Session请求则会创建新的实例,该bean实例仅在当前Session内有效。

    <bean id="userPreference" class="com.ioc.UserPreference" scope="session"/>
    

    同Http请求相同,每一次session请求创建新的实例,而不同的实例之间不共享属性,且实例仅在自己的session请求内有效,请求结束,则实例将被销毁。

  5. global Session:在一个全局的Http Session中,容器会返回该Bean的同一个实例,仅在使用portlet context时有效。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值