Android 原型模式

原型的是一种创建的设计模式,主用来创建的复杂的对象和构建耗时的实例。通过克隆已有的对象来创建的新的对象,从而节省时间和内存。

感谢

原型的模式介绍

原型模式的uml图

- Client 客户端类调用的类

- Prototype 提供clone()等方法的原型抽象接口

- ConcretePrototype 具体的原型的函数

原型的栗子

案例分析:多账号系统管理

我们需要做一个的类似的多用户登陆的简单系统,我们只能够在我的里面去创建的用户,并修改用户的属性,再其他的地方我们只能使用的这个的用户,不能够去修改这个用户的属性,但是随着的代码的迭代,可能在后期的维护过程中,有人会修改用户的信息,从而造成整体的问题。所以我们需要在其他的地方去使用的这个对象的副本。所以在这里我们可以使用原型模式。

package的截图

  • AccountManager :管理的Account, 负责的生成的Account,返回root Account,以及提供副本的Account 对象。
  • Account : ConcretePrototype
  • Client : 客户端调用的Account的类。
  • Cloneable : Prototype

Account model 类的代码

典型的model类,实现的Cloneable

public class Account implements Cloneable{
    private int id;
    private String name;
    private int age;
    private String desc;

    public Account(int id,int age,String name,String desc){
        this.id = id;
        this.age = age;
        this.name = name;
        this.desc = desc;
    }

    public Account clone(){
        Account clone = null;
        try {
            clone = (Account) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return clone;
    }   

    public int getId(){
        return id;
    }

    public String getName(){
        return name;
    }

    public int getAge(){
        return age;
    }

    public String getDesc(){
        return desc;
    }
}

AccountManager

管理的Account,提供最原始的以及生成新的副本的Account。

public class AccountManager {
    public static AccountManager sAccountManager;

    private AccountManager(){}

    public static AccountManager getAccountManager(){
        if(sAccountManager==null){
            synchronized(AccountManager.class){
                if(sAccountManager==null){
                    sAccountManager = new AccountManager();
                }
            }
        }
        return sAccountManager;
    } 

    private Map<Integer,Account> accountMap = new HashMap<Integer,Account>();

    public synchronized Account newAccount(int id,int age,String name,String desc){
        Account account = accountMap.containsKey(id) ? accountMap.get(id) : new Account(id,age,name,desc);
        accountMap.put(id, account);
        return account;
    }

    public Account getAccountById(int id) throws Exception{
        if(!accountMap.containsKey(id)){
            throw new Exception("没有当前的id的帐号");
        }

        return (Account) accountMap.get(id).clone();
    }

    public Account getRootAccountById(int id) throws Exception{
        if(!accountMap.containsKey(id)){
            throw new Exception("没有当前的id的帐号");
        }

        return accountMap.get(id);
    }
}

Client 调用Account的客户端类

public class Client {
    public static void main(String[] args) {
        //我的界面,生成新的Account
        AccountManager.getAccountManager().newAccount(1, 13, "Rrtoyewx", "Rrtoyewx是一个长不大的孩子");

        //其他的界面中客户端去获取的account,不允许修改的account的属性。即使修改了也不能去的改变最原始的
        try {
            Account accountClone = AccountManager.getAccountManager().getAccountById(1);
            Account accountRoot = AccountManager.getAccountManager().getRootAccountById(1);

            System.out.println("age:"+accountClone.getAge());
            System.out.println("id:"+accountClone.getId());
            System.out.println("name:"+accountClone.getName());
            System.out.println("desc:"+accountClone.getDesc());
            System.out.println("accountClone==accountRoot是:"+(accountClone==accountRoot));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

从上面就可以看出的,当我们在其他的地方去得到clone的Account ,即使我们去修改其属性的的话,对我们的AccountManager管理的Account并没有产生影响。

原型模式的注意点

  1. 原型的模式必须要的又个提供克隆的主体。不然没办法进行clone的。
  2. clone产生的副本,不会走构造器的方法。
  3. cloned的效率要比通过构造器生成的对象的效率要高。所以在尝试多次生成数据结构复杂的对象的时候,考虑好情况下,尽量使用原型模式去创建的对象。
  4. 深度拷贝和浅拷贝。

深度拷贝和浅拷贝

  1. 浅拷贝实际是对成员变量所有的字段进行一次拷贝,对于8中常见的包装类,string类型
  2. 引用类型的拷贝

上述的栗子是一个典型浅拷贝,上面的Account类成员变量都是8中常见的包装类以及string类,完全的浅拷贝

如果对于上述的Account的类,我们多了一个成员字段Address,Address是一个引用的类型

Account 类

public class Account implements Cloneable{
    private int id;
    private String name;
    private int age;
    private String desc;
    private Address address;

    public Account(int id,int age,String name,String desc){
        this.id = id;
        this.age = age;
        this.name = name;
        this.desc = desc;
    }

    public Account(int id,int age,String name,String desc,Address address){
        this.id = id;
        this.age = age;
        this.name = name;
        this.desc = desc;
        this.address = address;
    }

    public int getId(){
        return id;
    }

    public String getName(){
        return name;
    }

    public int getAge(){
        return age;
    }

    public String getDesc(){
        return desc;
    }

    public Address getAddress(){
        return address;
    }

    public Account clone(){
        Account clone = null;

        try {
            clone = (Account) super.clone();
            clone.address = this.address.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return clone;
    }
}

Address 类

public class Address implements Cloneable {
    private String name;
    private String location;

    public Address(){}

    public Address(String name,String location){
        this.name = name;
        this.location = location;
    }
}

Client类

public class Client {
    public static void main(String[] args) {
        //我的界面,生成新的Account
        AccountManager.getAccountManager().newAccount(1, 13, "Rrtoyewx", "Rrtoyewx是一个长不大的孩子",new Address("北京","昌平"));

        //其他的界面中客户端去获取的account,不允许修改的account的属性。即使修改了也不能去的改变最原始的
        try {
            Account accountClone = AccountManager.getAccountManager().getAccountById(1);
            Account accountRoot = AccountManager.getAccountManager().getRootAccountById(1);

            System.out.println("clone age:"+accountClone.getAge());
            System.out.println("clone id:"+accountClone.getId());
            System.out.println("clone name:"+accountClone.getName());
            System.out.println("clone desc:"+accountClone.getDesc());
            System.out.println("clone address:"+accountClone.getAddress());
            System.out.println("address:"+accountRoot.getAddress());
            System.out.println("accountClone==accountRoot是:"+(accountClone==accountRoot));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这个Account 类中有个Address引用类型的address成员变量,这个时候还是采用软拷贝的方式,我们会看到打印的log

log图:

我们可以看到new Account对象Address对象的地址和Clone Account是一样,所以在从某种的意义上来说,还并没有保护好Account对象。因为我们修改了Clone Account的Address的地址,那么原生的Account的Address对象地址也同样会被修改了。所以我们这个时候需要采用了深度拷贝。我们对Address的类要做修改。

深拷贝的第一种方式,实现clone的方法

Address 实现Cloneable的接口,并重写clone()的方法。

public Address clone(){
        Address clone = null;
        try {
            clone = (Address) super.clone();
            clone.location = this.location;
            clone.name = this.name;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return clone;
    }

Account类clone()的方法。

public Account clone(){
        Account clone = null;

        try {
            clone = (Account) super.clone();
            clone.address = this.address.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        return clone;
    }   

深拷贝的第二种方式,序列化的方式

Address 定义的deepclone的方法,并通过对象流写入和读出的操作实现的复制的功能。

public Address deepClone() throws IOException, ClassNotFoundException{
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos =  new ObjectOutputStream(baos);
        oos.writeObject(this);

        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);

        return (Address) ois.readObject();
    }

Account类的修改

public Account clone(){
        Account clone = null;

        try {
            clone = (Account) super.clone();
            clone.address = this.address.deepClone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        } catch(IOException e){
            e.printStackTrace();
        } catch(ClassNotFoundException e){
            e.printStackTrace();
        }

        return clone;
    }   

无论是第一种clone深度拷贝还是第二种的序列化的深度拷贝打印的log都为下图

原型模式的一些简单的封装

上面我们知道原型模式存在深拷贝和浅拷贝,所以对于model包下的类,我们不能简单概括为一个clone的方法,我们区分深拷贝和浅拷贝的。

所以在实际的使用情况,我们可以自定义一个PrototypeAble的接口。

PrototypeAbled接口:

public interface PrototypeAble extends Cloneable,Serializable{

    PrototypeAble lightClone() throws CloneNotSupportedException;

    PrototypeAble deepClone() throws CloneNotSupportedException, ClassNotFoundException, IOException;

    PrototypeAble root();
}

当我们定义好这样的一个接口后,我们对上面的栗子进行修改如下,

AccountPrototype类

public class AccountPrototype implements PrototypeAble{
    private int id;
    private String name;
    private int age;
    private String desc;
    private Address address;

    public AccountPrototype(int id,int age,String name,String desc,Address address){
        this.id = id;
        this.age = age;
        this.name = name;
        this.desc = desc;
        this.address = address;
    }

    @Override
    public PrototypeAble lightClone() throws CloneNotSupportedException {
        return (PrototypeAble) super.clone();
    }

    @Override
    public PrototypeAble deepClone() throws CloneNotSupportedException, ClassNotFoundException, IOException {
        AccountPrototype clone = null;

        clone = (AccountPrototype) super.clone();
        clone.address = this.address.deepClone();

        return clone;
    }

    @Override
    public PrototypeAble root() {
        return this;
    }

    @Override
    public String toString() {
        return "AccountPrototype [id=" + id + ", name=" + name + ", age=" + age + ", desc=" + desc + ", address="
                + address + "]";
    }
}

AddressPrototype类

public class AddressPrototype implements PrototypeAble {
    private String name;
    private String location;

    @Override
    public PrototypeAble lightClone() throws CloneNotSupportedException {
        return (PrototypeAble) super.clone();
    }

    @Override
    public PrototypeAble deepClone() throws CloneNotSupportedException {
        return (PrototypeAble) super.clone();
    }

    @Override
    public PrototypeAble root() {
        return this;
    }
}

AccountManager类

public class AccountManager {
    public static AccountManager sAccountManager;

    private Map<Integer,AccountPrototype> accountMap = new HashMap<Integer,AccountPrototype>();

    private AccountManager(){}

    public static AccountManager getAccountManager(){
        if(sAccountManager==null){
            synchronized(AccountManager.class){
                if(sAccountManager==null){
                    sAccountManager = new AccountManager();
                }
            }
        }
        return sAccountManager;
    } 

    public synchronized AccountPrototype newAccount(int id,int age,String name,String desc,Address address){
        AccountPrototype account = accountMap.containsKey(id) ? accountMap.get(id) : new AccountPrototype(id,age,name,desc,address);
        accountMap.put(id, account);
        return account;
    }

    public AccountPrototype getDeepCloneAccountById(int id) throws Exception{
        if(!accountMap.containsKey(id)){
            throw new Exception("没有当前的id的帐号");
        }

        return (AccountPrototype) accountMap.get(id).deepClone();
    }

    public AccountPrototype getLightCloneAccountById(int id) throws Exception{
        if(!accountMap.containsKey(id)){
            throw new Exception("没有当前的id的帐号");
        }

        return (AccountPrototype) accountMap.get(id).lightClone();
    }

    public AccountPrototype getRootAccountById(int id) throws Exception{
        if(!accountMap.containsKey(id)){
            throw new Exception("没有当前的id的帐号");
        }

        return (AccountPrototype) accountMap.get(id).root();
    }
}

client类

public class Client {
    public static void main(String[] args) {
        //我的界面,生成新的Account
        AccountManager.getAccountManager().newAccount(1, 13, "Rrtoyewx", "Rrtoyewx是一个长不大的孩子",new Address("北京","昌平"));

        //其他的界面中客户端去获取的account,不允许修改的account的属性。即使修改了也不能去的改变最原始的
        try {
            AccountPrototype deepCloneAccount = AccountManager.getAccountManager().getDeepCloneAccountById(1);
            AccountPrototype lightCloneAccount = AccountManager.getAccountManager().getLightCloneAccountById(1);
            AccountPrototype rootAccount = AccountManager.getAccountManager().getRootAccountById(1);

            System.out.println("deepCloneAccount: "+deepCloneAccount.toString());
            System.out.println("lightCloneAccount: "+lightCloneAccount.toString());
            System.out.println("rootAccount: "+rootAccount.toString());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这个时候类的结构图已经正如我们一开始uml的图表示那样的,

- Client 客户端类调用的类,如我们此时的Client的类

- Prototype 提供clone()等方法的原型抽象接口,相当于我们的PrototypeAble的接口

- ConcretePrototype 具体的原型的函数,如栗子种的AccountPrototype和AddressPrototype类

- AccountManager Account的管理类

打印的结果也如下图

打印的结果也正如我们前面说的那样。

总结

至此我们已经将原型模式说完。记住

1. 原型模式是一种创建模式

2. 原型模式适用于构建较复杂,耗时较长的对象

3. 原型模式不经过构造方法

4. 使用原型模式区分好深拷贝和浅拷贝

5. 对于简单的对象,不建议使用的原型模式

时间: 2024-10-11 13:54:49

Android 原型模式的相关文章

Android设计模式(三)- 原型模式

原型模式也是一种创建型设计模式,从名字就能理解,这个模式应该有一个样板实例,也就是原型,然后用户从这个原型中复制出一个内部属性一致的实例,也就是克隆. 有时,一个对象的构造比较复杂并且比较耗时时,直接从已有对象复制一个实例比重新构造出来更高效. 简书地址 定义 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象. 使用场景 对象的初始化要消耗非常多的资源,包括硬件,数据等.可以使用原型模式避免这种资源的消耗. 用new来实例化一个对象时需要非常繁琐的数据准备或访问权限时,可以使用原型模

Android开发中无处不在的设计模式——原型模式

不知不觉这个系列已经写了三篇了,其实很早之前就想写设计模式了,只不过怕自己误人子弟没有提笔去写.后来在实际开发中,发现设计模式可以让一个开发人员融会贯通所学的知识,为了进一步巩固自己,就写下了这一些列文章.前面介绍了三个模式. Android开发中无处不在的设计模式--单例模式 Android开发中无处不在的设计模式--Builder模式 Android开发中无处不在的设计模式--观察者模式 本篇文章介绍的模式其实很简单,即原型模式,按照惯例,先看定义. 用原型实例指定创建对象的种类,并通过拷贝

Android源码分析之原型模式

模式的定义 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象. 使用场景 1.类初始化需要消化非常多的资源,这个资源包括数据.硬件资源等,通过原型拷贝避免这些消耗: 2.通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式: 3.一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝. UML类图 角色介绍 Client  :  客户端用户. Prototype : 抽象类或者接口,声明

Android设计模式--原型模式

1.定义: 用原型实例指定创建对象种类,并通过拷贝这些原型创建新的对象. 2.目的: 从一个对象创建另外一个可定制的对象,而不需要知道任何创建细节. 3.作用: 3.1.简化对象的创建: 3.2 .对于处理大对象,性能上比new 高出很多. 4.分类: 4.1浅拷贝:拷贝对象中的基本的数据类型,对于数组.容器对象.引用对象等都不会拷贝. 4.2深拷贝:将所有类型进行拷贝. 5.注意: 5.1对象实现Cloneable接口,必须将Object clone() 方法改为public: 5.2对于基本

Android设计模式系列--原型模式

CV一族,应该很容易理解原型模式的原理,复制,粘贴完后看具体情况是否修改,其实这就是原型模式.从java的角度看,一般使用原型模式有个明显的特点,就是实现cloneable的clone()方法.原型模式,能快速克隆出一个与已经存在对象类似的另外一个我们想要的新对象. 1.意图用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.热门词汇:克隆 深拷贝 浅拷贝 2.结构图和代码它的结构图非常简单,我们以Intent为例子: Intent的clone方法非常简单: @Override pu

设计模式系列(二)原型模式

在设计模式系列(一)单例模式 中详细介绍了单例设计模式,下面一起来看一下原型模式. 一.概述 原型模式是一种创建型设计模式,它通过复制一个已经存在的实例来返回新的实例,而不是新建实例.被复制的实例就是我们所称的原型,这个原型是可定制的. 原型模式多用于创建复杂的或者耗时的实例, 因为这种情况下,复制一个已经存在的实例可以使程序运行更高效,或者创建值相等,只是命名不一样的同类数据. 二.深拷贝和浅拷贝 原型模式中的拷贝分为"浅拷贝"和"深拷贝": 浅拷贝: 对值类型的

第九话-原型模式

有钱啦,有钱啦,是买MX4PRO还是6?知道我是怎么有那么多money的么?请学习原型模式. 一.什么是原型模式? 原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 注:类是抽象的,对象是具体的. 换句话说:你有一份简历(具体的),你想要10份,那就以此份为原件复印10份(具体的),OK. 二.为什么要用原型模式? 1.解决复杂类变动的困扰问题: 2.快速实例对象. 三.例子(简历) Main.java public class Main { public static

【设计模式】原型模式(Prototype)

摘要: 1.本文将详细介绍原型模式的原理和实际代码中特别是Android系统代码中的应用. 纲要: 1. 引入原型模式 2. 原型模式的概念及优缺点介绍 3. 原型模式对拷贝的使用 4. 原型模式在Android源码中的应用 1.先来一个段子: GG和MM经常在QQ上聊天,但是GG打字的速度慢如蜗牛爬行,每次MM在瞬间完成恢复或者问候是,GG都会很紧张的去尽力快速打字,尽管如此,还是让MM有些不高兴,MM说回复信息这么慢,显然是用心不专,不在乎她.哎,GG也是百口难辩啊,不过也确实是没有办法.

设计模式之原型模式(Prototype)

1.初识原型模式 大家都知道连锁机构是现在灰常流行的商业模式,比如咖啡之翼,那么假设咖啡之翼要在长春新建立一个分店,所经营的产品和以前在其他的城市已经存在的店经营的产品差不多,那么面向对象开发的角度怎么解决这个问题呢?难道要重新的实例化一个咖啡之翼的店??这显然不太好吧,咖啡之翼里面经营的产品(假设是属性吧)都需要重新写,这就是在做大量的重复工作啊,这显然是不符合OO开发思想的.遇到这样的情况,并不是重新建立一个类来解决这样的问题,而是通过设计模式中的"原型模式"来解决这种问题.是这种