LOADING

加载过慢请开启缓存 浏览器默认开启

设计模式-Prototype原型模式

2023/7/18 java DesignPattern
  1. Prototype原型模式
    1. what
    2. why
    3. structure
    4. how
    5. 浅拷贝和深拷贝
    6. when
    7. P&Cs
    8. summary

Prototype原型模式

what

原型模式是一种创建型设计模式, 使你能够复制已有对象, 而又无需使代码依赖它们所属的类。

why

如果你有一个对象,并希望生成与其完全相同的一个复制品,首先,你必须新建一个属于相同类的对象。 然后,你必须遍历原始对象的所有成员变量, 并将成员变量值复制到新对象中。

  • 但有个小问题。并非所有对象都能通过这种方式进行复制, 因为有些对象可能拥有私有成员变量, 它们在对象本身以外是不可见的。
  • 还有另外一个问题。你必须知道对象所属的类才能创建复制品, 所以代码必须依赖该类
  • 即使你可以接受额外的依赖性, 那还有另外一个问题: 有时你只知道对象所实现的接口, 而不知道其所属的具体类, 比如可向方法的某个参数传入实现了某个接口的任何对象。

structure

原型模式

how

为所有支持克隆的对象声明了一个通用接口,该接口让你能够克隆对象,同时又无需将代码和对象所属类耦合。
通常情况下, 这样的接口中仅包含一个克隆方法

创建一个当前类的对象, 然后将原始对象所有的成员变量值复制到新建的类中。
甚至可以复制私有成员变量,因为绝大部分编程语言都允许对象访问其同类对象的私有成员变量。

支持克隆的对象即为原型。当你的对象有几十个成员变量和几百种类型时, 对其进行克隆甚至可以代替子类的构造

具体例子:

public class Employees implements Cloneable{
    private List<String> empList;
    public Employees(){	empList = new ArrayList<String>();}
    public Employees(List<String> list){this.empList=list;}
    public void loadData(){	//模拟从数据库中加载数据
        empList.add("Java");
        empList.add("Python");
        empList.add("JavaScript");
        empList.add("PHP");
    }
    
    public List<String> getEmpList() {	return empList;}
    
    // 实现clone方法用于对象的复制
    @Override
    public Object clone() throws CloneNotSupportedException{
            List<String> temp = new ArrayList<String>();
            for(String s : this.getEmpList()){
                temp.add(s);
            }
            return new Employees(temp);
    }
}

clone()方法用于对Employees的深拷贝

public class PrototypePatternTest {

    public static void main(String[] args) throws CloneNotSupportedException {
        Employees emps = new Employees();
        emps.loadData();
        //使用克隆方法克隆出新对象
        Employees empsNew = emps.clone();
        List<String> list = empsNew.getEmpList();
        list.add("HTML");
        
        System.out.println("emps List: "+emps.getEmpList());
        System.out.println("empsNew List: "+list);
    }
}

浅拷贝和深拷贝

  • 浅拷贝:当拷贝对象只包含简单的数据类型比如int、float 或者不可变的对象(字符串)时,就直接将这些字段复制到新的对象中。而引用的对象并没有复制而是将引用对象的地址复制一份给克隆对象
  • 深拷贝:不管拷贝对象里面简单数据类型还是引用对象类型都是会完全的复制一份到新的对象中

针对浅拷贝和深拷贝的工具类

  • 深拷贝(deep copy):SerializationUtils
  • 浅拷贝(shallow copy):BeanUtils

when

  • ArrayList的克隆:1.先克隆 2.再将原型中的数据拷贝到新的ArrayList中

  • 针对平台百万、千万、甚至上亿的用户发送通知的时候通知的内容基本都是一样的只是推送用户不一样或者有些特别字段值的小改动,那我们这里就可以用原型模式来做,同时开启多线程来做push,需要注意的是这里的线程安全问题,所以在每个线程内部去做copy对象。

P&Cs

P:

  • 克隆对象无需与它们所属的具体类相耦合。
  • 克隆预生成原型, 避免反复运行初始化代码。
  • 方便地生成复杂对象。
  • 可以用继承以外的方式来处理复杂对象的不同配置。

C:

  • 克隆包含循环引用的复杂对象可能会非常麻烦。

summary

  • 原型模式使用简单,但是在每次clone基类 或者 有引用对象 的时候需要去修改原型对象的clone方法,这不符合开闭原则
  • 在一般情况下是不建议用这种模式的除非创建的对象成本特别大,或者在一些特殊场景使用