Android常见设计模式——原型模式(Prototype Pattern)

前言

23种常见设计模式概述——GoF 23(四人帮,Gang of Four)一文中提到过设计模式可以分为三种类型,分别为

  • 创建型模式
  • 结构型模式
  • 行为型模式

1. 原型模式

这里介绍的原型模式(Prototype Pattern)属于第一种类型,即创建型模式。对于创建型模式,侧重点也还是关注于如何生成新的对象。在原型模式中,通过复制一个现有的对象来生成新的对象,而不是通过实例化的方式。当复制一个对象时,新的对象将拥有与原对象相同的属性和方法。被复制的实例就是我们所称的“原型”。

在创建一个对象比较复杂或者构造比较耗时的时候,复制一个现有的实例可以提高程序的运行效率。

1.1 适用场景

  • 类初始化需要消耗比较多的资源,通过原型拷贝可以避免这些消耗;
  • 通过new产生一个对象需要非常繁琐的数据准备或者访问权限,使用原型拷贝可以避免;
  • 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值,可以考虑使用原型拷贝来复制多个对象给调用者;

2. 原型模式的简单实现

import java.util.ArrayList;

public class Man extends Human implements Cloneable{
    
    
    private ArrayList<Human> familys;

    public Man(){
    
    
    	System.out.println("--------------执行了Man的构造函数--------------");
        familys = new ArrayList<>();
        this.userAge = 0;
        this.userName = "0";
    }

    @Override
    protected Man clone() throws CloneNotSupportedException {
    
    
        Man man = (Man) super.clone();
        man.userName = this.userName;
        man.userAge = this.userAge;
        man.familys = this.familys;
        return man;
    }

    public void setFamilys(ArrayList<Human> familys) {
    
    
        this.familys = familys;
    }
    
    public ArrayList<Human> getFamilys() {
    
    
        return familys;
    }

    public void showUserInfo(){
    
    
        System.out.println("-----------------------------------");
        System.out.println("姓名:" + this.userName);
        System.out.println("年龄:" + this.userAge);
        System.out.print("家庭成员:");
        for (Human family : familys) {
    
    
            System.out.print(family.userName+"\t");
        }
        System.out.println();
    }
}

class Human{
    
    
    // 定义为protected子类中是可访问
    protected String userName;
    protected int userAge;

    public Human(){
    
    }

    public Human(String userName, int userAge){
    
    
        this.userAge = userAge;
        this.userName = userName;
    }

    public void setUserName(String userName) {
    
    
        this.userName = userName;
    }

    public void setUserAge(int userAge) {
    
    
        this.userAge = userAge;
    }
}

测试:

public class RandomizedSet {
    
    

    public static void main(String[] args) throws CloneNotSupportedException {
    
    
        
        Man man = new Man();
        man.setUserName("张三");
        man.setUserAge(21);
        ArrayList<Human> familys = new ArrayList<>();
        familys.add(new Human("张怡", 43));
        familys.add(new Human("刘宝", 50));
        man.setFamilys(familys);
        man.showUserInfo();

        Man clone = man.clone();
        clone.showUserInfo();

        clone.setUserName("王五");
        clone.getFamilys().add(new Human("王二", 23));
        clone.showUserInfo();

        man.showUserInfo();
    }
}

结果:
在这里插入图片描述

从上面的案例当中可以看到:拷贝后的对象和原对象的普通属性再是一个内存空间,因为基本类型不需要进行克隆。而字符串类型为常量,一旦定义出不同的字符串就会自动分配一块新的存储空间,也不会公用同一块存储空间。但是对于ArrayList这种引用类型属性,还是使用的同一个空间。注意到通过clone进行拷贝对象的时候,不会再执行类的构造函数。

其实也就是说上述拷贝为一个浅拷贝,并不是将所有字段都重新构造了一份副本,而是使用引用来指向同一个地址空间。如果需要解决这个问题,我们可以重新修改下clone函数:

@Override
protected Man clone() throws CloneNotSupportedException {
    
    
    Man man = (Man) super.clone();
    man.userName = this.userName;
    man.userAge = this.userAge;
    man.familys = (ArrayList<Human>) this.familys.clone();
    return man;
}

再次执行结果为:
在这里插入图片描述


References

  • 《Android源码设计模式解析与实战》

Guess you like

Origin blog.csdn.net/qq_26460841/article/details/120636897