设计模式之原型模式---prototype

原型模式的定义

原型模式(Prototype pattern):
Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this 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<String> heroSkills = new ArrayList<String>();

    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<String> 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<String>) 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方法的原理是从内存中以二进制流
  • 深复制和浅复制
    使用原型模式时,引用的成员变量必须满足两个条件才不会被复制。一是类的成员变量,而不是方法内变量,二是必须是一个可变的引用对象,而不是一个原始类型或不可变的对象

android 原型模式的样例

Intent 类

源码位置:frameworks/base/core/java/android/content/Intent.java
(1)Intent类的原型模式的关键代码:

public class Intent implements Parcelable, Cloneable {
.........................
/**
* Copy constructor.
*/
public Intent(Intent o) {
   this.mAction = o.mAction;
   this.mData = o.mData;
   this.mType = o.mType;
   this.mPackage = o.mPackage;
   this.mComponent = o.mComponent;
   this.mFlags = o.mFlags;
   this.mContentUserHint = o.mContentUserHint;
   if (o.mCategories != null) {
       this.mCategories = new ArraySet<String>(o.mCategories);
   }
   if (o.mExtras != null) {
        this.mExtras = new Bundle(o.mExtras);
   }
   if (o.mSourceBounds != null) {
         this.mSourceBounds = new Rect(o.mSourceBounds);
   }
   if (o.mSelector != null) {
          this.mSelector = new Intent(o.mSelector);
    }
    if (o.mClipData != null) {
        this.mClipData = new ClipData(o.mClipData);
     }
}

@Override
public Object clone() {
    return new Intent(this);
}
................................
}

从代码中,我们可以看到,Intent类是实现Cloneable接口,在clone方法中,通过Intent的构造方法来返回Intent类型的对象。并且非常明显,Intent是深复制。

(2)Intent使用样例

Intent intent = new Intent(MainActivity.this, InsertContactsActivity.class);
Intent intentCloneIntent = (Intent) intent.clone();
startActivity(intentCloneIntent);

Bundle类

源码地址:
frameworks/base/core/java/android/os/Bundle.java
(1)Bundle关键类

public final class Bundle extends BaseBundle implements Cloneable, Parcelable {

    private boolean mHasFds = false;
    private boolean mFdsKnown = true;

    /**
     * Constructs a Bundle containing a copy of the mappings from the given
     * Bundle.
     *
     * @param b a Bundle to be copied.
     */
    public Bundle(Bundle b) {
        super(b);
        mHasFds = b.mHasFds;
        mFdsKnown = b.mFdsKnown;
    }
    /**
     * Clones the current Bundle. The internal map is cloned, but the keys and
     * values to which it refers are copied by reference.
     */
    @Override
    public Object clone() {
        return new Bundle(this);
    }
..............
}

(2)Bundle使用样例
在一个activity中发送带Bundle的Intent:

Bundle newExtrasBundle = new Bundle();
Bundle newExtras = (Bundle)newExtrasBundle.clone();
newExtras.putString("circleCrop", "true");
newExtras.putBoolean("return-data", true);
Intent cropIntent = new Intent(MainActivity.this, OtherActivity.class);
cropIntent.putExtras(newExtras);
startActivity(cropIntent);

在OtherActivity中接受到此Intent:

Intent intent = getIntent();
Bundle bundle = intent.getExtras();
String circleCrop = bundle.getString("circleCrop");
Boolean returnDataBoolean = bundle.getBoolean("return-data");
textView.setText("circleCrop:"+circleCrop+"---returnDataBoolean:"+returnDataBoolean);

参考资料

1.Android设计模式源码解析之原型模式
https://github.com/hfreeman2008/android_design_patterns_analysis/tree/master/prototype/mr.simple
2.设计模式之禅之原型模式

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hfreeman2008

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值