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. 对于简单的对象,不建议使用的原型模式

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、home桌面规则及划屏操作 1.1home桌面规则 桌面页数最大为9屏,开机默认显示第1屏即时钟widget。 1.2桌面划屏操作 (请用鼠标划屏体验) 桌面页面可划屏切换,切换的效果为桌面滚动。每向左划一次屏则桌面亦向左滚动一屏,页码栏的小圆点前进一个页码,同时背景图要向左移动微量移动;向右则相反。提示页码的小圆点在页码即将变化时呈逐级缩放的渐变效果,缩放级数为四级。 2、主菜单(应用程序列表)的切换及划屏操作 2.1 桌面与主菜单界面切换 (请用鼠标点击界面底部的“主菜单/主页“图标体验) 点界面底部Launcher快捷栏的主菜单图标切换到主菜单界面。主菜单界面从下方弹出(注意过渡效果,弹出时间为300ms)。默认的页码为第1页。主菜单背景图以从哪个桌面进入就以哪个桌面的背景为背景图,并叠加一半透明灰色效果以区别于home桌面背景。进入主菜单后再划屏,背景位置不变。 2.3主菜单界面划屏操作 (请用鼠标划屏体验) 划屏时可循环切换 ,即持续往一个方向划屏时会循环显示各页面。(请用鼠标操作体验)。划屏过程中在页码即将改变时,页码栏的小圆点发生四级缩放的变化。 3、页码栏的操作 3.1home桌面页码栏操作 (请用鼠标点击小圆点体验) 表示页码的小圆点桌面页码栏支持点击操作。点击小圆点时,被点击的小圆点逐级放大并显示页码数字;与此同时显示点击之前页码数字的小圆点逐级缩小且数字消失。同时屏幕会滚动到该页。 3.2主菜单页码栏操作 页码栏只支持点击操作。效果同2.1。 4、多点触控(pinch)操作(请用鼠标点击右边的手势模拟效果) 桌面或主菜单界面都支持pinch操作。pinch动作后会进入缩略图界面。点击缩略图即可进入相应的页面。这里要做成逐级缩放的效果。即pinch动作后,当前页面逐级缩小(分六至八级),透明度亦逐级变小,最后展现缩略图界面。点击缩略图时,相应的页面缩略图则逐级放大,透明度亦逐级放大,最后展现普通界面。 缩略图超过4页以九宫格排列。低于4页则以4宫格排列。在桌面缩略图界面可进行桌面管理,如桌面重排、桌面隐藏、桌面恢复、桌面增加和删除等。详见动画演示文档。 5、Launcher快捷栏操作(请用鼠标拖动体验) Launcher快捷栏的图标分左右两页,每页四个图标。可左右拖动,支持循环切换,即持续拖动时会循环显示两页内容(请拖动试试)。 在home界面点“主菜单”图标,会切换到主菜单界面。同时该图标变成“主页”(注意两页同时变化)。在主菜单界面点“主页”图标,则切换到home桌面,且该图标变成“主菜单”。(请点击体验) Launcher快捷栏除“通话”、“信息”、“联系人”、“主页/主菜单“图标不可替换外,其它图标可替换。替换方法详见动画演示文档。 6、menu键、back键、home键 从桌面进menu菜单,可更换壁纸(背景图),添加桌面内容(widget、应用、文件夹等 (演示 略) 主界面点 back 返回桌面。(演示 略) 长按home键,显示最近使用过的应用程序。(演示 略)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值