1.原型
1.1定义
原型模式:使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。
原型模式的定义说明:
-
工作原理:将一个原型对象传给要发动创建的对象(即客户端对象),这个要发动创建的对象通过请求原型对象复制自己来实现创建过程
-
创建新对象(也称为克隆对象)的工厂就是原型类自身,工厂方法由负责复制原型对象的克隆方法来实现
-
通过克隆方法所创建的对象是全新的对象,它们在内存中拥有新的地址,每一个克隆对象都是独立的
-
通过不同的方式对克隆对象进行修改以后,可以得到一系列相似但不完全相同的对象
1.2角色
原型模式包含以下3个角色:
- Prototype(抽象原型类=带克隆功能的抽象产品类)
- ConcretePrototype(具体原型类=实现抽象原型类)
- Client(客户类)
1.3优点
- 简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率
- 扩展性较好
- 提供了简化的创建结构,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品
- 可以使用深克隆的方式保存对象的状态,以便在需要的时候使用,可辅助实现撤销操作
1.4缺点
- 需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时,需要修改源代码,违背了开闭原则
- 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦
1.5适用环境
- 创建新对象成本较大,新对象可以通过复制已有对象来获得,如果是相似对象,则可以对其成员变量稍作修改
- 系统要保存对象的状态,而对象的状态变化很小
- 需要避免使用分层次的工厂类来创建分层次的对象,并且类的实例对象只有一个或很少的几个组合状态,通过复制原型对象得到新实例可能比使用构造函数创建一个新实例更加方便
1.6教学例子
(1)分析:
-
孙悟空:根据自己的形状复制(克隆)出多个身外身
-
软件开发:通过复制一个原型对象得到多个与原型对象一模一样的新对象
-
与工厂模式相比,取消了工厂,换句话说,工厂和产品合二为一
(2)类图:
(3)代码:
浅克隆
public class Weapon implements Cloneable{
private String name;
private int hurtValue;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the hurtValue
*/
public int getHurtValue() {
return hurtValue;
}
/**
* @param hurtValue the hurtValue to set
*/
public void setHurtValue(int hurtValue) {
this.hurtValue = hurtValue;
}
public void showWeapon(){
System.out.println("武器名:"+this.name+";伤害值:"+this.hurtValue);
}
public Weapon clone(){
Weapon weapon = null;
try{
weapon = (Weapon) super.clone();
}catch(Exception e){
e.printStackTrace();
}
return weapon;
}
}
深克隆
public class Sunwukong implements Cloneable{
public Sunwukong(){
System.out.println("从灵石中蹦出来");
}
private String name;
private String address;
private int age;
private Weapon weapon;
/**
* @return the weapon
*/
public Weapon getWeapon() {
return weapon;
}
/**
* @param weapon the weapon to set
*/
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the address
*/
public String getAddress() {
return address;
}
/**
* @param address the address to set
*/
public void setAddress(String address) {
this.address = address;
}
/**
* @return the age
*/
public int getAge() {
return age;
}
/**
* @param age the age to set
*/
public void setAge(int age) {
this.age = age;
}
public void show(){
System.out.println("俺叫'"+this.name+"',住在'"+this.address+"',今年"+this.age+"岁。");
}
public Sunwukong clone(){
Sunwukong sunwukong = null;
try{
sunwukong = (Sunwukong) super.clone();
//sunwukong.weapon = sunwukong.weapon.clone();
sunwukong.setWeapon(sunwukong.weapon.clone());
}catch(Exception e){
e.printStackTrace();
}
return sunwukong;
}
}
Client(客户类)
public class Client {
public static void main(String[] args) {
Sunwukong qitiandasheng = new Sunwukong();
qitiandasheng.setName("齐天大圣");
qitiandasheng.setAddress("花果山水帘洞");
qitiandasheng.setAge(600);
Weapon jingubang = new Weapon();
jingubang.setName("金箍棒");
jingubang.setHurtValue(10000);
qitiandasheng.setWeapon(jingubang);
qitiandasheng.show();
qitiandasheng.getWeapon().showWeapon();
List<Sunwukong> monkeyArmy = new ArrayList<Sunwukong>();
for(int i = 0; i<600;i++){
// Sunwukong monkey = new Sunwukong();
// monkey.setName("齐天大圣");
// monkey.setAddress("花果山水帘洞");
// monkey.setAge(600);
//
// monkey.show();
Sunwukong monkey = qitiandasheng.clone();
monkey.setName("猴子兵"+String.valueOf(i));
monkey.setAddress("花果山水帘洞"+String.valueOf(i)+"号洞");
monkey.setAge((int)(Math.random()*100));
// Weapon shaohuogun = jingubang.clone();
// shaohuogun.setName("烧火棍"+String.valueOf(i));
// shaohuogun.setHurtValue((int)(Math.random()*100));
// monkey.setWeapon(shaohuogun);
monkey.getWeapon().setName("烧火棍"+String.valueOf(i));
monkey.getWeapon().setHurtValue((int)(Math.random()*100));
monkeyArmy.add(monkey);
}
monkeyArmy.get(145).show();
monkeyArmy.get(145).getWeapon().setName("烧火棍1");
monkeyArmy.get(145).getWeapon().setHurtValue(89);
monkeyArmy.get(145).show();
monkeyArmy.get(145).getWeapon().showWeapon();
monkeyArmy.get(301).show();
monkeyArmy.get(301).getWeapon().showWeapon();
monkeyArmy.get(450).show();
monkeyArmy.get(450).getWeapon().showWeapon();
qitiandasheng.show();
qitiandasheng.getWeapon().showWeapon();
}
}
1.7实操案例
(1)问题:
某在线招聘网站中,用户可以创建一个简历模板,针对不同的工作岗位,可以复制该简历模板并进行适当修改后,生成一份新的简历,在复制简历时,用户可以选择是否复制简历中的照片,如果选择“是“,则照片将一同被复制。且用户对新简历中的照片进行修改不会影响到简历模板中的照片。选择”否“时,将引用建立模板中的照片,这是用户对新建立中的照片进行修改时,会将模板中的照片一同修改。请选择一种合适的设计模式来设计该简历模块,画出类图并模拟实现。
(2)类图:
(3)代码:
浅克隆:
public class Resume implements Cloneable{
private String name="赵本山";
private int age=21;
private Photo p;
public Photo getP() {
return p;
}
public void setP(Photo p) {
this.p = p;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void show(){
System.out.println("姓名:"+this.name+"年龄:"+this.age);
}
public Resume clone(boolean bool){
Resume ca = null;
try {
ca = (Resume) super.clone();
if(bool){
ca.setP(ca.getP().clone());
}else{
ca.setP(ca.getP());
}
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return ca;
}
}
深克隆:
public class Photo implements Cloneable{
private String photo = "^&^";
public void setPhoto(String photo) {
this.photo = photo;
}
public void show (){
System.out.println(this.photo);
}
public Photo clone(){
Photo p = null;
try {
p = (Photo) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}
Client(客户端)
public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
Resume re1 = new Resume();
Photo p = new Photo();
re1.setP(p);
System.out.println("---原型对象---");
re1.show();
re1.getP().show();
Resume re2 = re1.clone(true);
System.out.println("---克隆对象---");
re2.setName("黄俊银");
re2.show();
re2.getP().show();
System.out.println("---克隆对象修改后头像---");
re2.getP().setPhoto("@~@");
re2.getP().show();
System.out.println("---克隆修改后原型对象头像---");
re1.getP().show();
}
}