您的位置:首页 > 其它

设计模式-原型模式

2016-03-14 21:30 357 查看

1.原型模式的介绍

原型模式是一个创建型的模式。原型二字表明了该模式应该有一个样板的实例,用户从这个样板对象中复制出一个内部属性一致的对象,这个过程也就是我们俗称的“克隆”。被复制的实例就是我们所称的“原型”,这个原型是可以定制的。原型模式多用于创建复杂的或者构造耗时的实例,因为这种情况下 ,复制一个已经存在的实例可使程序运行的更高效。

2.原型模式的定义

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

3.原型模式的使用场景

3.1 类初始化需要消耗非常多的资源,这资源包括数据、硬件资源等,通过原型拷贝避免这些消耗。
3.2 通过new产生一个对象需要非常繁琐的数据准备或访问权限,这时可以使用原型模式。
3.3 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝。

4.UML类图



5.深拷贝和浅拷贝

5.1 深拷贝(深克隆)深拷贝把要复制的对象所引用的对象复制了一遍
5.2 浅拷贝(浅克隆)浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。
5.3 Java中对象的克隆,为了获取对象的一份拷贝,可以利用Object类的clone()方法。必须遵循下面三点:
A.在派生类中覆盖基类的clone()方法,并声明为public[Object类中的clone()放为
protected]
B.在派生类中的clone()方法中,调用super.clone()。
C.在派生类中实现Cloneable接口

6.new 和 clone 的区别

  new操作符的本意是分配内存。程序执行到new操作符时, 首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。而clone在第一步是和new相似的, 都是分配内存,调用clone方法时,分配的内存和源对象(即调用clone方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域,
填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。

7.实现

1.创建一个商场进货单对象A,含有商场名称和水果。另外一家商场B想按照商场A进货,现在只需将进货单的商场名称修改即可。
1.1.原型类
public class Mark implements Cloneable{
String name;
ArrayList<String> list = new ArrayList<>();

public Mark() {
}

protected Mark clone() {
try {
Mark mark = (Mark) super.clone();
mark.name = this.name;
mark.list = this.list;
return mark;
} catch (Exception e) {

}

return null;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public ArrayList<String> getList() {
return list;
}

public void setList(ArrayList<String> list) {
this.list = list;
}

public void addFruit(String fruit) {
this.list.add(fruit);
}

public void showMark() {
System.out.println("/***********" + name + " Start***********/");
System.out.println("Nmae : " + name);
for (String fruit: list) {
System.out.println("fruit: " + fruit);
}
System.out.println("/************" + name + " End************/");
}
}


1.2.测试类
public class TestPrototype {
public static void main(String[] args) {
Mark mark_0 = new Mark();
mark_0.setName("家乐福");
mark_0.addFruit("苹果");
mark_0.addFruit<span style="font-family: Arial, Helvetica, sans-serif;">("梨");</span>
mark_0.addFruit("橙汁");
mark_0.showMark();

Mark mark_1 = mark_0.clone();
mark_1.showMark();

mark_1.setName("华润万家");
mark_1.showMark();

mark_0.showMark();
}
}

1.3.输出结果
/***********家乐福 Start***********/
Nmae : 家乐福
fruit : 苹果
fruit : 梨
fruit : 橙汁
/************家乐福 End************/
/***********家乐福 Start***********/
Nmae : 家乐福
fruit : 苹果
fruit : 梨
fruit : 橙汁
/************家乐福 End************/
/***********华润万家 Start***********/
Nmae : 华润万家
fruit : 苹果
fruit : 梨
fruit : 橙汁
/************华润万家 End************/
/***********家乐福 Start***********/
Nmae : 家乐福
fruit : 苹果
fruit : 梨
fruit : 橙汁
/************家乐福 End************/


mark_1是通过mark_0.clone()创建的。根据输出结果,可以明显看出mark_0和mark_1的
输出结果是一致的,即mark_1是mark_0的一份拷贝。在mark_1中修改了商场的名称,并不影响
mark_0的内容。
需要注意的是通过clone()拷贝对象并不会执行构造函数。

2.商场B想在进货单中添加西瓜和火龙果两种水果,是否对商场A有影响呢?
2.1 测试类
public class TestPrototype {
public static void main(String[] args) {
Mark mark_0 = new Mark();
mark_0.setName("家乐福");
mark_0.addFruit("苹果");
mark_0.addFruit("梨");
mark_0.addFruit("橙汁");
mark_0.showMark();

Mark mark_1 = mark_0.clone();
mark_1.showMark();

mark_1.setName("华润万家");
mark_1.showMark();

mark_1.addFruit("西瓜");
mark_1.addFruit("火龙果");
mark_1.showMark();

mark_0.showMark();
}
}


2.2 输出结果
/***********家乐福 Start***********/
Nmae : 家乐福
fruit : 苹果
fruit : 梨
fruit : 橙汁
/************家乐福 End************/
/***********家乐福 Start***********/
Nmae : 家乐福
fruit : 苹果
fruit : 梨
fruit : 橙汁
/************家乐福 End************/
/***********华润万家 Start***********/
Nmae : 华润万家
fruit : 苹果
fruit : 梨
fruit : 橙汁
/************华润万家 End************/
/***********华润万家 Start***********/
Nmae : 华润万家
fruit : 苹果
fruit : 梨
fruit : 橙汁
fruit : 西瓜
fruit : 火龙果
/************华润万家 End************/
/***********家乐福 Start***********/
Nmae : 家乐福
fruit : 苹果
fruit : 梨
fruit : 橙汁
fruit : 西瓜
fruit : 火龙果
/************家乐福 End************/

通过输出结果,可以明显看到商场A的进货单在商场B修改的同时也修改了。这是怎么回事呢?在前面我们提过深拷贝和浅拷贝的区别,在上文中Mark的clone()方法只是简单地进行了浅拷贝,mark_1中的引用类型指向了mark_0的引用list并没有重新构造一个对象,然后再mark_1将西瓜和火龙果添加至引用list时,实际上是将其添加到mark_0中的引用对象中,意味着mark_0和mark_1的引用对象是同一个。现在发现了这个问题,该如何解决?之前引用的是浅拷贝,如果采用的深拷贝是什么效果,不妨试一下。
3
3.1 修改clone()方法,将引用对象也进行拷贝
protected Mark clone() {
try {
Mark mark = (Mark) super.clone();
mark.name = this.name;
mark.list = (ArrayList<String>) this.list.clone();
return mark;
} catch (Exception e) {

}

return null;
}
3.2 输出结果
/***********家乐福 Start***********/
Nmae : 家乐福
fruit : 苹果
fruit : 梨
fruit : 橙汁
/************家乐福 End************/
/***********家乐福 Start***********/
Nmae : 家乐福
fruit : 苹果
fruit : 梨
fruit : 橙汁
/************家乐福 End************/
/***********华润万家 Start***********/
Nmae : 华润万家
fruit : 苹果
fruit : 梨
fruit : 橙汁
/************华润万家 End************/
/***********华润万家 Start***********/
Nmae : 华润万家
fruit : 苹果
fruit : 梨
fruit : 橙汁
fruit : 西瓜
fruit : 火龙果
/************华润万家 End************/
/***********家乐福 Start***********/
Nmae : 家乐福
fruit : 苹果
fruit : 梨
fruit : 橙汁
/************家乐福 End************/
输出结果显示,在修改商场B的进货列表时,商场的进货列表并未受到影响。如上述代码所示,将mark.list指向this.list的一份拷贝,而不是mark.list本身。

8.总结

原型模式是一个非常简单的模式,它的核心问题是对原始对象进行拷贝,在这个模式中需要注意的是深拷贝和浅拷贝的问题。在实际运用中,为了避免不必要的错误,建议尽量使用深拷贝,避免对原始对象的影响.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: