zoukankan      html  css  js  c++  java
  • 设计模式(五)原型模式 Prototype

    • 原型模式: 

      原型模式,是指基于一个已经给定的对象,通过拷贝的方式,创建一个新的对象,这个给定对象,就是“原型”。

      在 Java 中,原型模式体现为 Object 的 clone() 方法。

      所有类都可以通过实现 Cloneable 接口,以及重写 clone() 方法,来实现原型模式。

      

    • 代码:
    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    public class Liability implements Cloneable {private String code;
        private String name;
        private String category;
        private boolean isMajor;
    
        @Override
        protected Liability clone() throws CloneNotSupportedException {
            return (Liability) super.clone();
        }
    }
    @Data
    @Builder
    public class PolicyShallowClone implements Cloneable {
    
        private String code;
        private int applicantAge;
        private Liability liability;
        private List<String> specialDescriptions;
    
        @Override
        public PolicyShallowClone clone() throws CloneNotSupportedException {
            return (PolicyShallowClone) super.clone();
        }
    }
    • 缩减 clone() 方法的返回类型:

      自 JDK1.5 开始,Java 引进了一个新的特性:协变返回类型(covariant return type)。

      即:覆盖方法的返回类型,可以是被覆盖方法的返回类型的子类。

      所以需要在 clone() 方法内部进行强转。

      这体现了一条通则:永远不要让客户去做任何类库能够替客户完成的事情。

    • clone() 是一种浅度复制(Shallow Copy):
        @Test
        void testPolicy1() throws Exception {
            // Build original policy
            Liability liability = new Liability.LiabilityBuilder().code("0001").name("Liability").category("XPXA").build();
            String specialDescription1 = "text1";
            String specialDescription2 = "text2";
            List<String> specialDescriptions = new ArrayList<>(Arrays.asList(specialDescription1, specialDescription2));
            PolicyShallowClone policyA = PolicyShallowClone.builder().specialDescriptions(specialDescriptions).liability(liability).code("code001").applicantAge(18).build();
            // Call clone
            PolicyShallowClone policyB = policyA.clone();
            Assertions.assertSame(policyA.getCode(), policyB.getCode());
            Assertions.assertEquals(policyA.getCode(), policyB.getCode());
            // Assert shallow clone
            policyA.getSpecialDescriptions().add("text3");
            Assertions.assertSame(policyA.getLiability(), policyB.getLiability());
            Assertions.assertTrue(policyA.getSpecialDescriptions().size() == policyB.getSpecialDescriptions().size());
        }
    • 编写一个优秀的 clone() 方法:

      克隆对象的数据来源,必须来自于 clone() 方法,所以永远在方法内部调用 super.clone() 方法。

      所有的父类必须很好地实现了 clone() 方法。

      如果当前类包含的域引用了可变对象,需要递归地调用 clone() 方法。

      如果在线程安全的类中实现 Cloneable 接口,clone() 方法必须得到很好的同步。

    • 一个深度复制的 clone() 方法:
    @Data
    @Builder
    public class PolicyDeepClone implements Cloneable {
    
        private String code;
        private int applicantAge;
        private Liability liability;
        private List<String> specialDescriptions;
    
        @Override
        public PolicyDeepClone clone() throws CloneNotSupportedException {
            PolicyDeepClone clone = (PolicyDeepClone) super.clone();
            clone.specialDescriptions = new ArrayList<>(this.specialDescriptions);
            clone.liability = this.liability.clone();
            return clone;
        }
    }
    • 深度复制的测试:
        @Test
        void testPolicy2() throws Exception {
            // Build original policy
            Liability liability = new Liability.LiabilityBuilder().code("0001").name("Liability").category("XPXA").build();
            String specialDescription1 = "text1";
            String specialDescription2 = "text2";
            List<String> specialDescriptions = new ArrayList<>(Arrays.asList(specialDescription1, specialDescription2));
            PolicyDeepClone policyA = PolicyDeepClone.builder().specialDescriptions(specialDescriptions).liability(liability).code("code001").applicantAge(18).build();
            // Call clone
            PolicyDeepClone policyB = policyA.clone();
            // Assert deep clone
            policyA.getSpecialDescriptions().add("text3");
            Assertions.assertNotSame(policyA.getLiability(), policyB.getLiability());
            Assertions.assertFalse(policyA.getSpecialDescriptions().size() == policyB.getSpecialDescriptions().size());
        }
    • 有必要这么复杂吗?

      从上述的介绍,我们不难发现,要完成一个优秀的 clone() 方法,存在诸多限制。

      并且,当我们实现了 clone() 方法,在编译器中,还会看到一条 Blocker 级别的 Sonar 警告:

      Remove this "clone" implementation; use a copy constructor or copy factory instead.

      它推荐的是一个拷贝构造器和拷贝工厂。

    • 拷贝构造器(Copy constructor)
    @Data
    @Builder
    public final class PolicyCopyConstructor {
    
        private String code;
        private int applicantAge;
        private Liability liability;
        private List<String> specialDescriptions;
    
        public PolicyCopyConstructor(PolicyCopyConstructor policy) {
            this.code = policy.code;
            this.applicantAge = policy.applicantAge;
            this.liability = policy.liability;
            this.specialDescriptions = policy.specialDescriptions;
        }
    }

      显然,这是一个浅度复制的实现,如果需要深度复制,需要深一步挖掘,这里不详述。

    • 拷贝工厂(Copy factory):
    @Data
    public final class PolicyCopyFactory {
    
        private String code;
        private int applicantAge;
        private Liability liability;
        private List<String> specialDescriptions;
    
        public static PolicyCopyFactory newInstance(PolicyCopyFactory policy) {
            PolicyCopyFactory copyPolicy = new PolicyCopyFactory();
            copyPolicy.setCode(policy.getCode());
            copyPolicy.setApplicantAge(policy.getApplicantAge());
            copyPolicy.setLiability(policy.getLiability());
            copyPolicy.setSpecialDescriptions(policy.getSpecialDescriptions());
            return copyPolicy;
        }
    }

      拷贝工厂本质上使我们之前提到过的静态工厂的一种变形。

      在这里,这也是浅度复制的实现。

    • Copy constructor & Copy factory 的优势:
    1. 不依赖于某一种带有风险的,语言之外的对象创建机制(clone 是 native 方法)。
    2. 不会与 final 域的正常使用发生冲突(clone 架构与引用可变对象的 final 域的正常使用是不兼容的)。
    3. 不会抛出受检异常。
    4. 不需要类型转换。
    • 《Effective Java》 第11条:谨慎地覆盖 clone

      鉴于 clone() 方法存在这么多限制,《Effective Java》明确指出:

      除了拷贝数组,其他任何情况都不应该去覆盖 clone() 方法,也不该去调用它。

    • 关于深复制:

      这篇文章 第004弹:几种通用的深度复制的方法 介绍了几种深复制的通用方法。

  • 相关阅读:
    vs2012无法启动已配置的开发Web服务器
    VS2013无法启动 IIS Express Web解决办法
    Windows server 2008系统基本优化
    Asp.net mvc项目架构分享系列之架构搭建初步
    sql windows server2008 全套激活码
    (copy)MVC4.0网站发布和部署到IIS7.0上的方法
    (转) C#多线程赛跑实例
    (已解决) 未能加载文件或程序集“Newtonsoft.Json, Version=4.0.0.0, Culture=neutral,
    C# HttpWebRequest 绝技 转至 http://www.sufeinet.com/
    string 与char* char[]之间的转换 2015-04-09 11:30 29人阅读 评论(0) 收藏
  • 原文地址:https://www.cnblogs.com/jing-an-feng-shao/p/7594680.html
Copyright © 2011-2022 走看看