原型模式:
1、定义:原型模式就是通过一个原型对象来表明要创建的对象类型,然后用复制这个对象的方法来创建更痛类型的对象。
2、原理:有两部分组成,抽象原型和具体原型。
3、使用时机:系统需要 创建吃的对象是动态加载的,而且产品具有一定层次时,可以考虑使用原型模式。
1>当要实例化的类是在运行时刻指定时,例如,通过动态装载;
2>或者为了避免创建一个与产品类层次平行的工厂类层次时;
3>或者当一个类的实例只能有几个不同状态组合中的一种时。
4>建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
4、效果:
1>可以再运行时刻增加和删除产品。
2>可以通过改变值来指定产品。
3>可以通过改变结构来指定新对象。
4>减少子类的构造
5>可以用类动态配置应用。
5、实现:
1>使用一个原型管理器
2>实现克隆操作(浅拷贝和深拷贝)
3>初始化克隆对象。
6、使用原型模式的意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
7、解决的问题:
比如有一个对象,在某一时刻该对象中已经包含了一些有效的值,此时可能会需要一个和该对象完全相同的新对象,并且此后对新对象的任何改动都不会影响到原来对象中的值,也就是说新对象与原来的对象是两个独立的对象,但新对象的初始值是由原来的对象确定的。
Clone:
赋值创建对象:
1>java中赋值创建对象是可以实现对象的重用的,但是新对象和原对象是同一个引用;如果修改其中的一个对象的值,则另外的一个对象也会发生改变。
2>使用clone方法会返回对象的一个拷贝,这样一来,如果修改一个对象的值,则另外的对象不会发生改变的。
原型模式UML图:
请注意,在 这边Cloneable并非指Java中的java.lang.Cloneable,而是指支持原型复制 的对象,必须实作之公开协议。
不 同的语言可能提供不同程 度支持之对象复制技术,以Java而言,java.lang.Object本身即定义有clone()方法,因此所有的对象 基本上 皆具自我复制之能力,不 过真正要让对象支持复制,则对象必须实作java.lang.Cloneable这个标示接口(Tag interface)。
原型模式浅拷贝与原型模式深度拷贝:
看看这样一个示例:有个类(DogClone)实现了Java的Cloneable接口,也实现了Object的clone()方法,它持有另一个没有实现Cloneable接口并且没有复写Object的clone()方法的引用(Dog)。如果Dog同时实现了clone()方法也实现了Cloneable接口,在对DogClone做科隆操作的时候会不会影响dog的值呢?
- package org.bestupon.prototype.copy;
- /**
- * 同实现了克隆方法的类和实现克隆方法的类做比较
- * @author bestupon
- * 浅拷贝Dog
- */
- public class Dog {
- /**
- * 狗腿条数
- */
- public int legCounts;
- public Dog(int legCounts) {
- this.legCounts = legCounts;
- }
- /**
- * 改变狗的腿数量
- */
- public void changeLegCounts(){
- this.legCounts *=2;
- }
- public String toString () {
- return Integer.toString(this.legCounts);
- }
- }
- package org.bestupon.prototype.clone;
- /**
- * 该类是没有实现克隆方法的类和实现克隆方法的类做比较
- * @author bestupon
- * 深度拷贝用例
- */
- public class Dog implements Cloneable{
- /**
- * 狗腿条数
- */
- public int legCounts;
- public Dog(int legCounts) {
- this.legCounts = legCounts;
- }
- /**
- * 改变狗的腿数量
- */
- public void changeLegCounts(){
- this.legCounts *=2;
- }
- @Override
- public Dog clone() throws CloneNotSupportedException {
- return (Dog)super.clone();
- }
- public String toString () {
- return Integer.toString(this.legCounts);
- }
- }
- package org.bestupon.prototype.copy;
- public class DogClone implements Cloneable {
- /**
- * 狗腿条数
- */
- public int legCounts;
- /**
- * 初始化一个狗
- */
- Dog dog = new Dog(4);
- @Override
- protected DogClone clone() throws CloneNotSupportedException {
- return (DogClone)super.clone();
- }
- }
- <span style="white-space: normal;"> <span style="white-space: pre;">package org.bestupon.prototype.copy;</span></span>
- /** 浅拷贝
- * @author bestupon
- *
- */
- public class Client {
- public static void main(String args []) throws CloneNotSupportedException {
- DogClone dogClone = new DogClone();
- dogClone.legCounts = 3;
- System.out.println("原来的克隆狗腿数量:"+dogClone.legCounts);
- System.out.println("原来的普通狗腿的数量:"+dogClone.dog);//Dog的toString方法返回的值。
- DogClone dogClone1 = (DogClone)dogClone.clone();
- dogClone1.legCounts=2 ;
- Dog dog = dogClone1.dog;
- dog.changeLegCounts();
- System.out.println("克隆后原来狗腿数量:"+dogClone.legCounts);
- /**
- * 出现的结果是:8
- * 原因很简单就是dog是一个引用,改变一个对象的话,会改变另一个对象。
- */
- System.out.println("克隆后原来普通狗的数量:"+ dogClone.dog);
- System.out.println("克隆后克隆狗腿的数量:"+ dogClone1.legCounts);
- /**
- *改变源:改变了自身dogClone.dog,影像了对象dogClone.dog 的值,
- */
- System.out.println("克隆后普通狗的数量:"+ dogClone1.dog);
- }
- }
两次运行结果:
没有实现Cloneable接口和clone()方法结果:
- 原来的克隆狗腿数量:3
- 原来的普通狗腿的数量:4
- 克隆后原来狗腿数量:3
- 克隆后原来普通狗的数量:8
- 克隆后克隆狗腿的数量:2
- 克隆后普通狗的数量:8
实现了Cloneable接口和clone()方法结果:
- 原来的克隆狗腿数量:3
- 原来的普通狗腿的数量:4
- 克隆后原来狗腿数量:3
- 克隆后原来普通狗的数量:4
- 克隆后克隆狗腿的数量:2
- 克隆后普通狗的数量:8
同样的客户端为什么会有不同的结果呢?这就是所谓的浅拷贝和深拷贝的为题.
分析:
浅拷贝: DogClone类中的legCounts属性被科隆了,因为对科隆后的对象进行修改时,没有改变原来对象的值,其原因是实现了接口和方法,而DogClone,Dog对象 dog没有被科隆,而在修改科隆后的对象时,会改变原来对象的值。
如果要是都实现了接口和方法,就实现了深度科隆,不会影响远对象的值。
应用:
不是所有的对象都是能实现深度科隆的,例如:StringBuffer就没有重载Clone()方法,而且StringBuffer还是一个final类,所以其不能自动实现深度科隆。
在Spring中.深度科隆是应用广泛的。在实体模型上面有这样一个注解:@Component("lawsuitArchive") @scope("prototype"),表明在别的地方应用的时候,是引用其原型,同时Entity也要实现Cloneable接口和复写clone()方法。因为原型在初始化的时候就已经创建了一部分有价值的东西,减少容器的压力,而在别的地方,例如action中引用该对象的时候,直接可以使用@Resource(name="lawsuitArchive")引用对象,Spring会自动的装配好你想要的对象。
--------------------------------------------------------------------
reference 2:
The Prototype Pattern
The Prototype pattern is known as a
creational
pattern, as it is used to construct objects suchthaey they can be decoupled from their implementing systems. The definition of Prototype as provided in the original Gang of Four book on Design Patterns states:
Create objects based on a template of an exsiting object through cloning.
In summary, instead of going to the trouble of creating object from scratch every time, you can make copies of an original instance and modify it as required.
The pattern is quite simple: the Prototype interface declares a method for cloning itself, while theConcretePrototype implements the operation for cloning itself.
In practice you will add in a registry to manage the finding and cloning of the objects. The detail of how it is used is best described in a code example.
When Would I Use This Pattern?
The Prototype pattern should be considered when
- Composition, creation and representation of objects should be decoupled from the system
- Classes to be created are specified at runtime
- You need to hide the complexity of creating new instance from the client
- Creating an object is an expensive operation and it would be more efficient to copy an object.
- Objects are required that are similar to existing objects.
So How Does It Work In Java?
Let's use a simple example in Java to illustrate this pattern. For this example, let's use a shopping cart example. Let's say that we have a number of items that can go in the cart - books, cds, dvds. While our example doesn't include anything that's particularly expensive to create, it should illustrate how the pattern works.
First, we'll create an abstract class for our Item, which will be our Prototype that includes a clone method.
01.
//Prototype
02.
public
abstract
class
Item
03.
{
04.
private
String title;
05.
private
double
price;
06.
07.
public
Item clone()
08.
{
09.
Item clonedItem =
null
;
10.
try
{
11.
//use default object clone.
12.
clonedItem = (Item)
super
.clone();
13.
//specialised clone
14.
clonedItem .setPrice(price);
15.
clonedItem.setTitle(title);
16.
}
catch
(CloneNotSupportedException e) {
17.
e.printStackTrace();
18.
}
// catch
19.
return
clonedItem ;
20.
21.
22.
}
23.
24.
public
String getTitle()
25.
{
return
title; }
26.
public
double
getPrice()
27.
{
return
price;}
28.
}
Next we'll create two ConcretePrototypes
01.
//Concrete Prototypes
02.
public
class
Book
extends
Item
03.
{
04.
//extra book stuff
05.
}
06.
07.
08.
public
class
CD
extends
Item
09.
{
10.
//extra cd stuff
11.
12.
}
Now let's create a registry for item creation
01.
public
class
ItemRegistry
02.
{
03.
private
Hashtable map =
new
Hashtable();
04.
05.
public
ItemRegistry
06.
{
07.
loadCache();
08.
}
09.
10.
11.
public
Item createBasicItem(String type)
12.
{
13.
return
map.get(type).clone();
14.
15.
}
16.
17.
private
void
loadCache()
18.
{
19.
Book book =
new
Book();
20.
book.setTitle(
"Design Patterns"
);
21.
book.setPrice(
20.00
);
22.
map.add(
"Book"
, book);
23.
24.
CD cd =
new
CD();
25.
cd.setTitle(
"Various"
);
26.
cd.setPrice(
10.00
);
27.
map.add(
"CD"
, cd);
28.
29.
}
30.
}
Finally, here's a Client that makes use of the Prototype. If we need to create a book, we can use the cached implementation to load it up, and modify it after.
01.
public
class
Client
02.
{
03.
04.
public
static
void
main(String[] args)
05.
{
06.
ItemRegistry registry =
new
ItemRegistry();
07.
Book myBook = registry.createBasicItem(
"Book"
);
08.
myBook.setTitle(
"Custom Title"
);
09.
//etc
10.
}
11.
12.
}