设计模式(五)----原型模式

概念

原型模式(Prototype Pattern)是用于创建重复对象,同时又能保证性能。
在应用程序当中,有些对象比较复杂,同时,又会频繁的利用到该对象,通过new创建这些对象的方式比较麻烦,又会消耗比较多的资源。这时,就可以用一个已有的对象进行复制,原型模式就可满足这个"复制"。
原型模式就是用一个原型对象,来指明所要创建对象的类型,然后通过原型对象所在类的复制对象的方法复制一个与原型对象同样的实例,这就是原型模式的设计目的。
这种模式是实现了一个原型接口,该接口用于创建当前对象的副本。当直接创建对象的代价比较大时,采用这种模式。如:一个对象需要在一个高代价的数据库查询后创建,这时,就可以先缓存该对象,在下一个请求时返回它的副本,在需要的时候更新数据库,以此来减少数据库的调用。

意图:用原型实例指定创建对象类型,通过拷贝这些原型生成新的对象。
主要解决:在运行期创建和删除原型。
何时使用:当一个系统独立于其产品创建,构成和表示时。当要实例化的对象的类是在运行时刻指定时,如通过动态装载。为避免创建一个与产品类层次平行的工厂类层次时。一个类的实例只有一种或少数几种组合状态。
如何解决:利用已有的原型对象,快速生成与原型对象一样的实例。
关键代码:继承Cloneable接口,重写clone()方法,实现克隆操作。

原型模式主要包含三个角色:
Prototype:抽象原型类(Object类)。声明克隆方法的接口,是所有具体原型类的公共父类,可以是抽象类,也可以是接口,还可以是具体实现类。
ConcretePrototype:具体原型类。实现克隆的具体操作,它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个副本对象。
Client:客户类。让一个原型克隆自身,获得一个新对象。在客户类中只需直接实例化或通过工厂模式等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。由于客户类针对抽象原型类Prototype编程,因此用户可根据需要选择具体原型类,所以原型模式使系统具有较好的扩展性,增加或更换具体原型类都很方便。

示例代码

利用原型模式模拟简历的复印
简历类(具体原型类)

public class Resume implements Cloneable {
	//个人基本信息
	private String name;
	private Integer age;
	private String gender;
	//个人工作经历
	private String timeArea;
	private String company;
	//省略getter、setter方法
	//设定个人基本信息
	public void setPersonInfo(String name,Integer age,String gender) {
		this.name = name;
		this.age = age;
		this.gender = gender;
	}
	//设定个人工作经历
	public void setWorkExperience(String timeArea,String company) {
		this.timeArea = timeArea;
		this.company = company;
	} 
	//复制对象方法
	public Object cloneObject() {
		Resume resume = null;
		try {
			resume = (Resume)super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return resume;
	}
	//打印简历信息
	public void printInfo() {
		System.out.println("个人基本信息:");
		System.out.println("姓名:" + name);
		System.out.println("年龄:" + age);
		System.out.println("性别:" + gender);
		System.out.println("个人工作经历:");
		System.out.println("工作年限:" + timeArea);
		System.out.println("公司:" + company);
	}
}

测试类(客户类)

public class Client {
	public static void main(String[] args) {
		Resume resume1 = new Resume();
		resume1.setPersonInfo("风清扬",26,"男");
		resume1.setWorkExperience("6年","阿里");

		Resume resume2 = (Resume)resume1.clone();
		System.out.println("简历一");
		resume1.printInfo();
		System.out.println();
		System.out.println("简历二");
		resume2.printInfo();

		System.out.println(resume1 == resume2);
		System.out.println(resume1.getClass() == resume2.getClass());
	}
}

打印结果
简历一
个人基本信息:
姓名:风清扬
年龄:26
性别:男
个人工作经历:
工作年限:6年
公司:阿里

简历二
个人基本信息:
姓名:风清扬
年龄:26
性别:男
个人工作经历:
工作年限:6年
公司:阿里
false
true

通过打印结果可以看出resume1和resume2是同一类型的对象,但却不是同一对象。

有关对象复制,包括深复制、浅复制和序列化反序列化的详细信息,可以参考我写的上一篇文章:
Java对象复制

原型管理器的引入和实现

原型管理器(Prototype Manager)是将多个原型对象存储在一个集合中供客户端使用,它是一个专门负责克隆对象的工厂。其中定义了一个集合用于存储原型对象,若需某个原型对象的一个副本,可通过复制集合中对应的原型对象来获得。在原型管理器中针对抽象原型类进行编程,以便扩展。
实例
创建一个抽象类Shape和扩展了Shape类的实体类。创建ShapeCache,该类把Shape类型的对象存储在一个Hashtable当中,并在请求的时候返回它们的副本。
创建实现Cloneable接口的抽象类(具体原型角色,实现用于复制现有实例生成新实例的方法)形状类Shape

public abstract class Shape implements Cloneable {
	private String id;
	protected String type;
	//省略对应属性的getter、setter方法

	abstract void draw();
	
	public Object clone() {
		Object object = null;
		try {
			object = super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return object;
	}
}

创建扩展抽象类的实体类
圆形类Circle

public class Circle extends Shape {
	public Circle() {
		type = "圆形";
	}
	@Override 
	public void draw() {
		System.out.println("圆形类的draw方法。。。");
	}
}

正方形类Square

public class Square extends Shape {
	public Square() {
		type = "正方形";
	}
	@Override
	public void draw() {
		System.out.println("正方形类的draw方法。。。");
	}
}

长方形类Rectangle

public class Rectangle extends Shape {
	public Rectangle() {
		type = "长方形";
	}
	@Override
	public void draw() {
		System.out.println("长方形的draw方法。。。");
	}
}

创建一个类,模拟从数据库中获取属性值,赋值给实体对象,并将这些实体对象存在一个Hashtable当中

public class ShapeCache {
	private static Hashtable<String,Shape> shapeMap = new Hashtable<String,Shape>();
	//从shapeMap当中获取指定原型对象的副本
	public static Shape getShape(String shapeId) {
		Shape cachedShape = shapeMap.get(shapeId);
		return (Shape)cachedShape.clone();
	}

	//对每种形状都运行数据库查询,并创建形状实体对象
	public static void loadCache() {
		Circle circle = new Circle();
		//从数据库查询属性值并赋值给对象
		circle.setId("1");
		shapeMap.put(circle.getId(),circle);

		Square square = new Square();
		//从数据库查询属性值并赋值给对象
		square.setId("2");
		shapeMap.put(square.getId(),square);

		Rectangle rectangle = new Rectangle();
		//从数据库查询属性值并赋值给对象
		rectangle.setId("3");
		shapeMap.put(rectangle.getId(),rectangle);
	}
}

测试类中使用ShapeCache类获取存储在Hashtable中的形状副本

public class PrototypePatternDemo {
	public static void main(String[] args) {
		ShapeCache.loadCache();
		Shape shape1 = ShapeCache.getShape("1");
		Shape shape2 = ShapeCache.getShape("2");
		Shape shape3 = ShapeCache.getShape("3");
		System.out.println("Shape:" + shape1.getType());
		System.out.println("Shape:" + shape2.getType());
		System.out.println("Shape:" + shape3.getType());
	}
}

打印结果
Shape:圆形
Shape:正方形
Shape:长方形

总结

原型模式优点
若新对象的创建比较复杂,消耗资源较多时,使用原型模式简化对象的创建过程,同时也能提高效率,逃避构造函数的约束。
扩展性较好,由于在原型模式中提供了抽象原型类,在客户端可针对抽象原型类进行编程,而将具体原型类写在配置文件当中,增加或减少产品类对原有系统无任何影响。
可使用深复制保存对象的状态。使用原型模式将对象复制一份并将其状态保存起来,以便在需要的时候使用(如恢复到某一历史状态),可辅助实现撤销操作。
原型模式提供了简化的创建结构。工厂模式需要有一个与产品类等级结构相同的工厂等级结构,而原型模式就不需要这样,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无需专门的工厂类创建产品。
原型模式缺点
需要为每一个类配备一个克隆方法,而且这个方法需要对类的功能进行通盘考虑,这对全新的类来说并不难,但对已有的类进行改造时,并非是一件容易的事,须修改其源码,违背了"开闭原则"。
深复制时,当对象之间存在多重嵌套时,每层对象所在的类须支持深复制。
使用场景
当创建新对象成本较大,可利用已有的对象进行复制来获得。
若系统要保存对象状态,而对象状态变化很小,或对象本身占用较少的内存时,可使用原型模式配合备忘录模式来使用。相反,若对象的状态变化很大,或对象占用内存很大,那么采用状态模式会比原型模式更好。
需避免使用分层次的工厂类创建分层次的对象,并且类的实例对象只有一个或几个较少的组合状态,通过复制原型对象得到新实例就比使用构造方法创建一个新实例更加方便。
资源优化场景。类的初始化需消耗较多的资源,包括数据、硬件资源等。性能和安全有要求的场景。通过new创建对象需繁琐的数据准备或访问权限,可使用原型模式。
一个对象多个修改者的场景,一个对象需提供该其他对象访问,并且多个对象都需要修改其值时,可使用原型模式复制多个对象供调用者使用。
实际项目中,原型模式很少单独使用,一般是配合工厂模式一起使用,通过clone()方法复制一个对象,然后由工厂方法提供给调用者。

原型模式向客户隐藏了对象创建的复杂性,客户只需知道创建对象的类型,然后通过请求就可获得与该对象一样的新对象,无需知道具体创建过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值