设计模式之-原型模式

原型模式定义

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象 
Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建


原型模式通用源码:

public class PrototypeClass implements Cloneable {

@Override
protected PrototypeClass clone(){
    // TODO Auto-generated method stub
    PrototypeClass prototypeClass = null; 
    try {
        prototypeClass = (PrototypeClass)super.clone();
    } catch (CloneNotSupportedException e) {
        // TODO: handle exception
        //process CloneNotSupportedException
    }
    return prototypeClass;
}

}

原型模式的实现

下面以一个英雄的样例来实现原型模式:

import java.util.ArrayList;
public class Hero implements Cloneable{
  private String name;
  private ArrayList heroSkills = new ArrayList();

    public Hero() {
        System.out.println(Hero 构造方法);
    }

    @Override
    protected Hero clone(){
        // TODO Auto-generated method stub
        try {
            Hero hero = (Hero) super.clone();
            hero.name = this.name;
            hero.heroSkills = this.heroSkills;
            return hero;
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }       
        return null;        
    }

    public String getName() {
        return name;
    }

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

    public ArrayList getHeroSkills() {
        return heroSkills;
    }

    public void addHeroSkills(String skillName) {
        if(!heroSkills.contains(skillName)){
            this.heroSkills.add(skillName); 
        }       
    }

    public void showInfo() {
        System.out.println(----------- Hero Start -----------);
        System.out.println(name :  + name);
        System.out.println(heroSkills: );
        for (String heroSkill : heroSkills) {
            System.out.println(hero Skill:  + heroSkill);
        }
        System.out.println(-----------Hero End -----------);
    }   

}

Hero类是一个英雄类,定义了名称和技能二个变量,Hero实现Cloneable接口的clone的方法。
Client端的使用方法:

public class Prototype {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Hero hero = new Hero();
        hero.setName(Sniper--火枪);
        hero.addHeroSkills(榴霰弹);
        hero.addHeroSkills(爆头);
        hero.addHeroSkills(瞄准);
        hero.addHeroSkills(暗杀);
        hero.showInfo();

        Hero heroClone = hero.clone();
        heroClone.showInfo();

        heroClone.setName(Sniper--火枪--第一把);
        heroClone.showInfo();

        hero.showInfo();
    }
}

运行方法,输入如下:

Hero 构造方法
----------- Hero Start -----------
name : Sniper--火枪
heroSkills: 
hero Skill: 榴霰弹
hero Skill: 爆头
hero Skill: 瞄准
hero Skill: 暗杀
-----------Hero End -----------
----------- Hero Start -----------
name : Sniper--火枪
heroSkills: 
hero Skill: 榴霰弹
hero Skill: 爆头
hero Skill: 瞄准
hero Skill: 暗杀
-----------Hero End -----------
----------- Hero Start -----------
name : Sniper--火枪--第一把
heroSkills: 
hero Skill: 榴霰弹
hero Skill: 爆头
hero Skill: 瞄准
hero Skill: 暗杀
-----------Hero End -----------
----------- Hero Start -----------
name : Sniper--火枪
heroSkills: 
hero Skill: 榴霰弹
hero Skill: 爆头
hero Skill: 瞄准
hero Skill: 暗杀
-----------Hero End -----------

从输出的信息,我们可以看出我们通过new一个对象和通过hero.clone()方法产生对象是一致的,并且通过clone方法产生的对象是不跑构造方法的。

深复制和浅复制
我们把Client改为:

    public static void main(String[] args) {
        // TODO Auto-generated method stub  
        Hero hero = new Hero();
        hero.setName(Sniper--火枪);
        hero.addHeroSkills(榴霰弹);
        hero.addHeroSkills(爆头);
        hero.addHeroSkills(瞄准);
        hero.addHeroSkills(暗杀);
        hero.showInfo();

        Hero heroClone = hero.clone();
        heroClone.showInfo();

        heroClone.addHeroSkills(火枪的第五个技能);
        heroClone.showInfo();

        hero.showInfo();    
    }

输出信息:

Hero 构造方法
----------- Hero Start -----------
name : Sniper--火枪
heroSkills: 
hero Skill: 榴霰弹
hero Skill: 爆头
hero Skill: 瞄准
hero Skill: 暗杀
-----------Hero End -----------
----------- Hero Start -----------
name : Sniper--火枪
heroSkills: 
hero Skill: 榴霰弹
hero Skill: 爆头
hero Skill: 瞄准
hero Skill: 暗杀
-----------Hero End -----------
----------- Hero Start -----------
name : Sniper--火枪
heroSkills: 
hero Skill: 榴霰弹
hero Skill: 爆头
hero Skill: 瞄准
hero Skill: 暗杀
hero Skill: 火枪的第五个技能
-----------Hero End -----------
----------- Hero Start -----------
name : Sniper--火枪
heroSkills: 
hero Skill: 榴霰弹
hero Skill: 爆头
hero Skill: 瞄准
hero Skill: 暗杀
hero Skill: 火枪的第五个技能
-----------Hero End -----------

从此输出信息,我们发现在通过hero.clone()产生的对象中添加了火枪的第五个技能,但是这个会存在原生的hero对象中。这个什么原因呢?
在clone方法中,我们采用的是:

hero.heroSkills = this.heroSkills;

这种方法,这是浅复制的方法。也就是说这种引用类型的新对象hero的变量heroSkills 单纯的指向了this.heroSkills 引用,而并没有进行复制。那要怎么样才能解决这个问题,主要是采取深复制的方法:即在复制对象时,对于引用型的字段也要采用copy的形式,而不是单纯引用的形式。

示例如下 :

protected Hero clone(){
    // TODO Auto-generated method stub
    try {
        Hero hero = (Hero) super.clone();
        hero.name = this.name;
        //hero.heroSkills = this.heroSkills;
        hero.heroSkills = (ArrayList) this.heroSkills.clone();
        return hero;
    } catch (CloneNotSupportedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }       
    return null;        
}

修改后的输出信息:

Hero 构造方法
----------- Hero Start -----------
name : Sniper--火枪
heroSkills: 
hero Skill: 榴霰弹
hero Skill: 爆头
hero Skill: 瞄准
hero Skill: 暗杀
-----------Hero End -----------
----------- Hero Start -----------
name : Sniper--火枪
heroSkills: 
hero Skill: 榴霰弹
hero Skill: 爆头
hero Skill: 瞄准
hero Skill: 暗杀
-----------Hero End -----------
----------- Hero Start -----------
name : Sniper--火枪
heroSkills: 
hero Skill: 榴霰弹
hero Skill: 爆头
hero Skill: 瞄准
hero Skill: 暗杀
hero Skill: 火枪的第五个技能
-----------Hero End -----------
----------- Hero Start -----------
name : Sniper--火枪
heroSkills: 
hero Skill: 榴霰弹
hero Skill: 爆头
hero Skill: 瞄准
hero Skill: 暗杀
-----------Hero End -----------

通过这种深复制的方法,可以让hero.heroSkills的变量也复制,从而让clone的方法复制的对象完全拥有一个完全独立于原型对象的heroSkills变量。

原型模式的优点

性能优良
原型模式是在内存二进制流的复制,要比new一个对象性能要好许多,特别是在一个循环体内产生大量的对象时,原型模式就可以更加显示其性能的优势 避免构造函数的约束
这既是它的优点也是缺点,直接在内存中复制对象,构造方法是不会执行的,优点是减少了约束,缺点也是减少了约束,大家要根据实际的情况来权衡。

原型模式的使用场景

资源优化场景
类初始化需要消耗非常多的资源,包括数据,硬件资源等 性能与安全要求的场景
通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式 一个对象多个修改者的场景
一个对象需要提供给其它对象访问,而且各个访问者可能都需要修改其值时,可以考虑使用原型模式复制多个对象供访问者使用。

原型模式注意事项

构造函数不会被执行
对象复制时构造函数没有执行,Object类的clone方法的原理是从内存中以二进制流 深复制和浅复制
使用原型模式时,引用的成员变量必须满足两个条件才不会被复制。一是类的成员变量,而不是方法内变量,二是必须是一个可变的引用对象,而不是一个原始类型或不可变的对象




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值