美文网首页
设计模式之原型模式(Prototype)

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

作者: ZHG | 来源:发表于2021-11-27 14:48 被阅读0次

    设计模式中有六大原则和二十三设计模式。
    其中六大原则分别为:单一职责原则、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特原则。
    二十三设计模式:单例模式、Builder 模式、原型模式、工厂方法模式、抽象工厂模式、策略模式、状态模式、责任链模式、解释器模式、命令模式、观察者模式、备忘录模式、迭代器模式、模版方法模式、访问者模式、中介模式、代理模式、组合模式、适配器模式、装饰模式、享元模式、外观模式、桥接模式。

    定义

    • 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。《Android 源码设计模式解析与实践》
    • 原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。[1]

    使用场景

    • 类初始化需要消耗很多资源(数据、硬件资源等)。
    • 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限。
    • 一个对象需提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象共调用者使用,即保护性拷贝。

    说到拷贝,我们再说下深拷贝和浅拷贝。

    深拷贝和浅拷贝

    • 浅拷贝
      创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
      常用的方法有:一一赋值、clone()等。

    需要注意的是:对于我们常用【=】它不属于浅拷贝,属于赋值,是同一个对象。如果是基本类型我们拷贝的是它的值;如果是对象则是拷贝的是对象内存地址的引用,其实他们还是同一个对象。

    • 深拷贝
      创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
      常用的方法有:层层拷贝(每个元素都需要实现clone()方法)、序列化反序列化、Gson 转换、构造函数(相当重新new一个对象,一一赋值)等

    示例

    • 对象类
    /**
     * 元素类
     */
    public class Children implements Cloneable {
        private String content;
    
        @Override
        protected Children clone() throws CloneNotSupportedException {
            return (Children)super.clone();
        }
    
        public String getContent() {
            return content;
        }
    
        public void setContent(String content) {
            this.content = content;
        }
    }
    /**
     * 原型类
     */
    public class Prototype implements Cloneable {
    
        private String message;
        private String title;
        private Children children;
        // ...
    
        @Override
        protected Prototype clone() throws CloneNotSupportedException {
            // 浅拷贝
            return (Prototype)super.clone();
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        public String getTitle() {
            return title;
        }
    
        public void setTitle(String title) {
            this.title = title;
        }
    
        public Children getChildren() {
            return children;
        }
    
        public void setChildren(Children children) {
            this.children = children;
        }
    }
    
    • 实现
        public static void main(String[] args) throws CloneNotSupportedException {
            Prototype prototype1 = new Prototype();
            prototype1.setTitle("Title1");
            prototype1.setMessage("Message1");
            Children children = new Children();
            children.setContent("Content1");
            prototype1.setChildren(children);
            System.out.println("prototype1------\n    hashCode = "+prototype1.hashCode()+"\n    title ="+prototype1.getTitle()+"\n    message="+prototype1.getMessage());
            System.out.println("Children1-------\n    hashCode = "+prototype1.getChildren().hashCode()+"\n    content="+prototype1.getChildren().getContent());
            Prototype prototype2 = prototype1.clone();
            System.out.println("prototype2------\n    hashCode = "+prototype2.hashCode()+"\n    title ="+prototype2.getTitle()+"\n    message="+prototype2.getMessage());
            System.out.println("Children2-------\n    hashCode = "+prototype2.getChildren().hashCode()+"\n    content="+prototype2.getChildren().getContent());
            prototype2.getChildren().setContent("Content2");
            System.out.println("prototype11------\n    hashCode = "+prototype1.hashCode()+"\n    title ="+prototype1.getTitle()+"\n   message="+prototype1.getMessage());
            System.out.println("Children11-------\n    hashCode = "+prototype1.getChildren().hashCode()+"\n    content="+prototype1.getChildren().getContent());
            System.out.println("prototype22------\n    hashCode = "+prototype2.hashCode()+"\n    title ="+prototype2.getTitle()+"\n   message="+prototype2.getMessage());
            System.out.println("Children22-------\n    hashCode = "+prototype2.getChildren().hashCode()+"\n    content="+prototype2.getChildren().getContent());
        }
    
    • 结果
    prototype1------
        hashCode = 531885035
        title =Title1
        message=Message1
    Children1-------
        hashCode = 1418481495
        content=Content1
    prototype2------
        hashCode = 303563356
        title =Title1
        message=Message1
    Children2-------
        hashCode = 1418481495
        content=Content1
    prototype11------
        hashCode = 531885035
        title =Title1
       message=Message1
    Children11-------
        hashCode = 1418481495
        content=Content2
    prototype22------
        hashCode = 303563356
        title =Title1
       message=Message1
    Children22-------
        hashCode = 1418481495
        content=Content2
    

    从上边的例子我们可以看出 Prototype 的 clone() 方法只是创建它自己本身新对象,而它的元素 Children 还是指向了原有的地址,这就是浅拷贝。要是想实现深拷贝,只需要重写 Prototype 的 clone() 方法。

        @Override
        protected Prototype2 clone() throws CloneNotSupportedException {
            // 浅拷贝
            Prototype2 prototype2 = (Prototype2)super.clone();
            prototype2.children = this.children.clone();
            return prototype2;
        }
    
    prototype1------
        hashCode = 531885035
        title =Title1
        message=Message1
    Children1-------
        hashCode = 1418481495
        content=Content1
    prototype2------
        hashCode = 303563356
        title =Title1
        message=Message1
    Children2-------
        hashCode = 135721597
        content=Content1
    prototype11------
        hashCode = 531885035
        title =Title1
       message=Message1
    Children11-------
        hashCode = 1418481495
        content=Content1
    prototype22------
        hashCode = 303563356
        title =Title1
       message=Message1
    Children22-------
        hashCode = 135721597
        content=Content2
    

    以上对原型模式(深拷贝、浅拷贝)进行了举例。接下来我们再说下它的优缺点。

    优缺点

    优点

    • 性能高:原型模式是在内存中二进制流的拷贝,要比直接new 一个对象性能好,特别是在循环体中产生大量对象时,
    • 简化流程:可以利用深拷贝,实现状态记录、回退等功能。

    缺点

    • 构造函数不会执行:原型模式是在内存中进行拷贝的,会跳过构造函数。
    • 实现繁琐:必须实现clone()方法
    • 拷贝风险:浅拷贝和深拷贝,一旦使用不正确,就会造成大灾难。

    总结

    浅拷贝和深拷贝有多种实现方法,这里只用一种方法举例,其他的方法可以参照深拷贝浅拷贝
    原型模式的宗旨就是对象的拷贝,节省创建多次创建新对象带来的消耗,提高性能。但是也需要注意深浅拷贝带来的问题。

    DEMO

    参考

    • 原型模式
    • 《Android 源码设计模式解析与实践》

    相关文章

      网友评论

          本文标题:设计模式之原型模式(Prototype)

          本文链接:https://www.haomeiwen.com/subject/lynfxrtx.html