原型模式,是指基於一個已經給定的對象,經過拷貝的方式,建立一個新的對象,這個給定對象,就是「原型」。html
在 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(); } }
自 JDK1.5 開始,Java 引進了一個新的特性:協變返回類型(covariant return type)。架構
即:覆蓋方法的返回類型,能夠是被覆蓋方法的返回類型的子類。app
因此須要在 clone() 方法內部進行強轉。ide
這體現了一條通則:永遠不要讓客戶去作任何類庫可以替客戶完成的事情。post
@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() 方法,因此永遠在方法內部調用 super.clone() 方法。測試
全部的父類必須很好地實現了 clone() 方法。ui
若是當前類包含的域引用了可變對象,須要遞歸地調用 clone() 方法。this
若是在線程安全的類中實現 Cloneable 接口,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.
它推薦的是一個拷貝構造器和拷貝工廠。
@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; } }
顯然,這是一個淺度複製的實現,若是須要深度複製,須要深一步挖掘,這裏不詳述。
@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; } }
拷貝工廠本質上使咱們以前提到過的靜態工廠的一種變形。
在這裏,這也是淺度複製的實現。
鑑於 clone() 方法存在這麼多限制,《Effective Java》明確指出:
除了拷貝數組,其餘任何狀況都不該該去覆蓋 clone() 方法,也不應去調用它。
這篇文章 第004彈:幾種通用的深度複製的方法 介紹了幾種深複製的通用方法。