Android中原型模式

设计模式系列:
        0. Android开发常用设计模式;

        1. Android中单例模式;

        2. Android中建造者(builder)模式;

        3. Android中观察者模式;

        4. Android中原型模式;

        5. Android中策略模式;

        6. Android中工厂模式;

        7. Android中代理模式;

        8. Android中装饰者模式;

        9. Android中适配器模式;


一、常见需求场景

  三毛:“小白,如下图,像淘宝里的商品,选择对应的颜色或尺寸就把该商品加入购物车,你觉得怎么去做呢”

这里写图片描述

二、基本解决方法

  小白:这个直接new一个商品对象,把选择的东西set进去就好了吖,就像人,假设只有名字不一样,其他都一样,不也是直接new一个对象就好了吗,像下面这样

for (int i = 0; i < 10; i++) {
    Person person = new Person();
    person.setName("我名字是:" + i);
   }

三、基本解决方法存在的问题

  三毛:”要是你上面那个对象属性很多也复杂,如果没加一个商品到购物车就new这样一个对象,岂不是很消耗内存和资源,造成卡顿什么的嘛”

  小白:但是我不能只new一个对象吧,肯定是一件商品new一个对象啊,好比人,如果改成下面这样,值new一个的话,每次setName都会把我前面的名字给修改了

 Person person = new Person();
 person.setName("我名字是0");
for (int i = 1; i < 10; i++) {
    person.setName("我名字是:" + i);
   }

  三毛:“你可以使用原型设计模式决解这类问题”

  小白:三毛哥,啥是原型模式?


四、原型模式写法

原型模式定义:原型模式是一种创建型的设计模式,通过已有的对象克隆出新的对象,使我们的程序运行的更高效。

  小白:这里写图片描述 怎么个克隆法捏

  三毛:“用一个对象实现Cloneable接口,复写clone方法,例如下面”

//解释一下:clone这个方法不是Cloneable接口中的clone是Object中的方法,Cloneable是一个标识接口,它表明这个类的对象是可以拷贝的。如果没有实现Cloneable接口却调用了clone()函数将抛出异常。(自己可以点击进去看下源码)

public class Person implements Cloneable {
    private String name;

     @Override
    protected Object clone(){
        Person person = null;
        try {
            person = (Person) super.clone();//克隆已有的对象
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        person.name = this.name;//把已有的属性值复制给克隆的对象里的属性
        return person;
    }

    public String getName() {
        return name;
    }

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


//然后你就可以使用了
 Person person = new Person();
 person.setName("我名字是0");
for (int i = 1; i < 10; i++) {
    Person p1= (Person) person.clone();
    p1.setName("我名字是:"+i);
   }

  小白: Person p1= (Person) person.clone()这样和我直接new出来有啥区别哇,完全不懂

  三毛:”你点击super.clone()进源码你会看到它的代码是下面这样的”

//super.clone()源码里面

protected Object clone() throws CloneNotSupportedException {
        if (!(this instanceof Cloneable)) {//这就是为嘛对象要实现Cloneable这个接口,不然给你抛异常
            throw new CloneNotSupportedException("Class " + getClass().getName() +
                                                 " doesn't implement Cloneable");
        }

        return internalClone();
    }

    /*
     * Native helper method for cloning.
     */

   //native方法,一般而言,对JVM来说,native方法的效率远比java中的普通方法高
   //所以才推荐你使用原型模式,而不是new
    private native Object internalClone();

  小白:喔,那原型模式有什么要注意的地方嘛

  三毛:“嗯,使用原型模式的时候要注意下面几点”

1)某些对象构造非常简单的情况下,就不要滥用原型模式了;
2)使用原型模式复制对象不会调用类的构造方法(new一个对象才会调用构造方法)。因为对象的复制是通过调用Object类的clone方法来完成的,它直接在内存中复制数据,也就是内存中二进制流的拷贝;
3)原型模式分为深拷贝和浅拷贝
浅拷贝:Object类的clone方法只会拷贝对象中的基本的数据类型(8种基本数据类型byte,char,short,int,long,float,double,boolean),
深拷贝:除了浅拷贝的8中基本数据类型,其他都属于深拷贝,例如数组、对象...

例如对象中有不属于8种基本数据类型时也用浅拷贝错误做法

//错误写法
//Person里有2个数据类型不属于浅拷贝的8种基本数据类型,却用了浅拷贝方式
public class Person implements Cloneable {
    private String name;
    private Action action;//action是个对象
    private ArrayList<String> listStr = new ArrayList<>();//数组
    @Override
    public Object clone(){
        Person person = null;
        try {
            person = (Person) super.clone();//克隆已有的对象
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        person.name = this.name;//把已有的属性值复制给克隆的对象里的属性
        person.action = this.action;//对象(引用),当成了基本数据类型处理
        person.listStr = this.listStr;//数组(引用),当成了基本数据类型处理
        return person;
    }
//set、get方法省略.....
}

正确写法

public class Person implements Cloneable {
    private String name;
    private Action action;//action是个对象
    private ArrayList<String> listStr = new ArrayList<>();//数组
    @Override
    public Object clone(){
        Person person = null;
        try {
            person = (Person) super.clone();//克隆已有的对象
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        person.name = this.name;//把已有的属性值复制给克隆的对象里的属性
        person.action = (Action) this.action.clone();//要调用clone
        person.listStr = (ArrayList<String>) this.listStr.clone();//要调用clone
        return person;
    }
}


//Action类
public class Action implements Cloneable{
    //省略属性
    @Override
    public Object clone(){
        Action action = null;
        try {
             action = (Action) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return action;
    }
}

  小白:毛毛哥,如果我使用上面的错误写法,结果会咋样吖

  三毛:使用错误写法的话会造成下面结果

                //克隆前的数据
                Person person = new Person();
                person.setName("小明");
                Action action = new Action();
                action.setActionType("吃饭");
                ArrayList<String> listStr = new ArrayList<>();
                listStr.add("好的");
                person.setAction(action);
                person.setListStr(listStr);
                System.out.println("---------原person---------");
                System.out.println(person.toString());

                //clone(克隆)一份后
                Person p = (Person) person.clone();//拷贝一个对象
                p.getAction().setActionType("睡觉");
                p.getListStr().add("不要");

                System.out.println("---------clone的person---------");
                System.out.println(p.toString());
                System.out.println("---------原person---------");



//打印的结果:
System.out: -----------原person-----------
Person{name='小明', action=Action{actionType='吃饭'}, listStr=[好的]}
System.out: -----------clone的person-----------
System.out: Person{name='小明', action=Action{actionType='睡觉'}, listStr=[好的, 不要]}
System.out: -----------原person-----------
System.out: Person{name='小明', action=Action{actionType='睡觉'}, listStr=[好的, 不要]}

//从上面我们可以看到clone后,给clone的对象设置新数据,我们原来的Person也给改变了,这不是我们想要的,我们只想设置clone的对象的值,并不想动原来Person的值

  小白:喔,我明白了,这个深拷贝就是是典型的嵌套,如果类里面有不是基本数据类型的就要像person.action = (Action) this.action.clone()对不是基本数据类型在一次clone

  三毛:“嗯,像在Android中Intent,Bundle也用到了原型模式,打开源码可以看到如下”

//源码里意图的原型模式写法
 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);
    }



//源码里Bundle原型模式写法
 public Bundle(Bundle b) {
        super(b);
        mFlags = b.mFlags;
    }

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

  小白:奥!怪不得以前看不懂,原来是原型模式啊

  三毛:“嗯,Android系统中很多地方都用到了原型模式,除此之外,像比较有名的OkHttp、realm数据库…都用到了原型模式呢”

五、原型模式和普通写法区别


1、每个clone出来的实例公共对象都是不同的,不会相互影响;

2、原型模式是在内存中二进制流的拷贝,要比直接 new 一个对象性能好很多(对象结构简单的就算了)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值