初三,旭日东升贴赤口,收口纳福平安顺。Seven祝大家人旺财旺运道旺。
本文脑图
01
原型模式
原型模式(Prototype Pattern)是一种简单的设计模式。
用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象。
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后才能创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
说白了就是以对象实例为原型模板复制新的对象。
02
包含角色
原型模式包含三个角色。
- 客户(Client)角色:该角色提出创建对象的请求。
- 抽象原型(Prototype)角色:该角色是一个抽象角色,通常由一个Java接口或抽象类实现,给出所有的具体原型类所需的接口。
- 具体原型(Concrete Prototype)角色:该角色是被复制的对象,必须实现抽象原型接口。
Java中内置了克隆机制,Object类具有一个clone()方法,能够实现对象的克隆,使一个类支持克隆需要以下两步。
- 实现Cloneable接口;
- 覆盖Object的clone()方法,完成对象的克隆操作,通常只需要调用Object的clone()方法即可。
为了使外部能够调用此类的clone()方法,可以将可访问性修改为public。因此,在Java中实现原型模式非常简单,让抽象原型Prototype接口继承 Cloneable 接口。
// 抽象原型
public interface Prototype extends Cloneable {
}
// 具体原型
public class ConcretePrototype implements Prototype {
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
// 客户角色
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建一个具体的原型模板对象
ConcretePrototype obj = new ConcretePrototype();
// 以该原型对象进行克隆
Object clone = obj.clone();
System.out.println(clone instanceof Prototype);
}
}
原型模式可以分为两种形式: 深拷贝和 浅拷贝。
深拷贝:当一个类的拷贝方法,不仅要复制对象的所有非引用成员变量值,还要为引用类型的成员变量创建新的实例,并且初始化为形式参数实例值。这个方式称为深拷贝。也就是说浅拷贝只复制一个对象,传递引用,不能复制实例。而深拷贝对对象内部的引用均复制,它是创建一个新的实例,并且复制实例。对于浅拷贝当对象的成员变量是基本数据类型时,两个对象的成员变量已有存储空间,赋值运算传递值,所以浅拷贝能够复制实例。但是当对象的成员变量是引用数据类型时,就不能实现对象的复制了。
03
原型模式的应用
我们先来看看原型模式的优缺点。
优点
- 性能优良:原型模式是在内存二进制流的复制,要比直接new一个对象性能好,特别是在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。
- 逃避构造函数的约束:这既是优点也是缺点,直接在内存中复制,构造函数是不会执行的,因此减少了约束,需要在实际应用时进行权衡考虑。
缺点
- 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。
- 必须实现 Cloneable 接口。
- 在实现深克隆时需要编写较为复杂的代码。
使用场景
- 资源优化场景。
- 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
- 性能和安全要求的场景。
- 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
- 一个对象多个修改者的场景。
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
- 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。
在框架中的应用实例
- 原型模式应用于很多软件中,如果每次创建一个对象要花大量时间,原型模式是最好的解决方案。很多软件提供的复制(Ctrl C)和粘贴(Ctrl V)操作就是原型模式的应用,复制得到的对象与原型对象是两个类型相同但内存地址不同的对象,通过原型模式可以大大提高对象的创建效率。
- 在Struts2中为了保证线程的安全性,Action对象的创建使用了原型模式,访问一个已经存在的Action对象时将通过克隆的方式创建出一个新的对象,从而保证其中定义的变量无须进行加锁实现同步,每一个Action中都有自己的成员变量,避免Struts1因使用单例模式而导致的并发和同步问题。
- 在Spring中,用户也可以采用原型模式来创建新的Bean实例,从而实现每次获取的是通过克隆生成的新实例,对其进行修改时对原有实例对象不造成任何影响。在每次使用对象之前,都会创建一个新的对象,并且会将依赖关系完整的赋值给这个新创建的对象。这样有利于节省系统资源,还可以更好地对原型管理器对象进行控制。
04
原型模式示例
使用原型模式模拟一个克隆邮件群发的实例。
@Data
public class Mail implements Cloneable {
public Mail(String subject, String context) {
this.subject = subject;
this.context = context;
}
// 收件人
private String receiver;
// 主题
private String subject;
// 称呼
private String appellation;
// 邮件内容
private String context;
// 邮件的尾部
private String tail;
@Override
public Mail clone() throws CloneNotSupportedException {
return (Mail) super.clone();
}
}
// 上述代码中,Mail类实现Cloneable接口,并实现了clone()方法,该方法是实现原型模式的关键,只有实现clone()方法,在应用中才能对Mail进行复制克隆。
public class MailClient {
public static void main(String[] args) throws CloneNotSupportedException {
Mail mail = new Mail("某商城春节抽奖活动", "春节抽奖通知:凡在春节期间在本商场购物满100元即可获得抽奖机会");
mail.setTail("本活动解析权归商城所有");
// 模拟发送给商城的所有用户,这里模拟5个
for (int i = 0; i < 5; i ) {
Mail cloneMail = mail.clone();
// 设置收件人和称呼,实际是从数据库获取的
cloneMail.setReceiver("zhangsan" i "@123.com");
cloneMail.setReceiver("zhangsan" i " 先生(女士)");
sendMail(cloneMail);
}
}
public static void sendMail(Mail mail) {
System.out.println("标题:" mail.getSubject() " 收件人:" mail.getReceiver() " 发送成功");
}
}
原型模式的分享介绍到这里就介绍完了,至此,创建型的所有设计模式全部介绍完了,我们可以了解到:
- 单例模式(Singleton)一个类只有一个实例,而且自行实例化并向整个系统提供这个实例;单例模式减少内存开支,避免对资源的多重占用,优化和共享源访问;但单例模式扩展难,不易测试;
- 工厂方法模式(Factory Method)定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类中;
- 抽象工厂模式(Abstract Factory)是工厂方法的升级,为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类;
- 建造者模式(Builder)也叫生成器模式,将一个复杂对象的构建与其表示分类,使得同样的构建过程可以创建不同的表示;
- 原型模式(Prototype)用原型实例指定创建对象的种类,并通过复制这些原型创建新的对象。
接下来进入结构型模式。
我是Seven,一个不懈努力的程序猿,希望本文能对你有所裨益
Seven的代码实验室
一只不懈努力的程序猿,通过代码实验洞悉技术的本质
,