一、原型模式
原型模式是用於創建重復的對象,同時又能保證性能。這種類型的設計模式屬於創建型模式。它提供了一種創建對象的最佳方式。
這種模式是實現一個原型接口,該接口用於創建當前對象的克隆。當直接創建對象的代價比較大時,則采用這種模式。例如:當一個對象需要在一個高代價的數據庫操作之后被創建。我們可以緩存該對象,在下一個請求返回它的克隆,在需要的時候更新數據庫,以此來減少數據庫的調用。
1,需求分析
現在有一只羊,姓名:tom,年齡:1,顏色:白。請編寫程序創建和tom羊屬性完全相同的10只羊。
2,傳統方式解決克隆羊
a)思路分析
b)代碼
源碼:詳情
public class Sheep { private String name; private int age; private String color; public Sheep(String name, int age, String color) { this.name = name; this.age = age; this.color = color; } //...get set方法 } public class Client { public static void main(String[] args) { Sheep sheep = new Sheep("tom", 1, "白色"); Sheep sheep1 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor()); Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor()); Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor()); } }
c)優缺點
- 優點:比較好理解,簡單易操作
- 確定:在創建新的對象時,總是需要重新獲取原始對象的屬性,如果創建的對象比較復雜時,效率比較低;總是需要重新初始化對象,而不是動態地獲得對象運行時的狀態,不夠靈活。
d)改進
Java中Object類時所有類的根類,Object類提供了一個clone()方法,該方法可以將一個Java對象復制一份,但是需要實現clone的Java類必須要實現一個接口Cloneable,該接口表示該類能夠復制且具有復制的能力 => 原型模式
3,原型模式
a)基本介紹
- 原型模式是指:用原型實例指定創建對象的種類,並且通過拷貝這些原型,創建新的對象(自我復制)
- 原型模式是一種創建型設計模式,允許一個對象再創建另外一個可定制的對象,無需知道如果創建的細節
- 工作原理:通過將一個原型對象傳給那個要發動創建的對象,這個要發動創建的對象通過請求原型對象拷貝它們自己來實施創建,即對象.clone()
b)原理結構圖
原理結構圖說明:
- Proptype:原型類,聲明一個克隆自己的接口
- ConcreteProptype:具體的原型類,實現一個克隆自己的操作
- Client:讓一個原型創建對象克隆自己,從而創建一個新的對象(成員屬性一樣)
c)采用原型模式解決克隆羊問題
源碼:詳情
public class Sheep implements Cloneable { private String name; private int age; private String color; public Sheep(String name, int age, String color) { this.name = name; this.age = age; this.color = color; } @Override protected Sheep clone() { try { return (Sheep) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } //...get set } public class Client { public static void main(String[] args) { Sheep sheep = new Sheep("tom", 1, "白色"); Sheep sheep1 = sheep.clone(); Sheep sheep2 = sheep.clone(); Sheep sheep3 = sheep.clone(); System.out.println("sheep = " + sheep); System.out.println("sheep1 = " + sheep1); System.out.println("sheep2 = " + sheep2); System.out.println("sheep3 = " + sheep3); } }
4,原型模式在Spring框架中的源碼分析
public class ProtoType { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); //獲取monster[通過id獲取monster] Object bean = applicationContext.getBean("id01"); System.out.println("bean="+bean); Object bean2 = applicationContext.getBean("id01"); System.out.println("bean="+bean); System.out.println(bean == bean2);//false:並不是同一個對象,只是兩個bean的屬性相同 } }
5,深拷貝與淺拷貝
a)淺拷貝
- 對於數據類型是基本數據類型的成員變量,淺拷貝會直接進行值傳遞,也就是將該屬性值復制一份給新的對象。
- 對於數據類型是引用數據類型的成員變量,比如說成員變量是某個數組、某個類的對象等,那么淺拷貝會進行引用傳遞,也就是只是將該成員變量的引用值(內存地址)復制一份給新的對象。因為實際上兩個對象的成員變量都指向同一個實例。在這種情況下,在一個對象中修改該成員變量會影響到另一個對象的成員變量值。
- 前面克隆羊就是淺拷貝,使用默認的clone()方法來實現:sheep=(Sheep)super.clone();
b)深拷貝
- 復制對象的所有基本數據類型的成員變量值
- 為所有引用數據類型的成員變量申請存儲空間,並復制每個引用數據類型成員變量所引用的對象,直到該對象可達的所有對象。也就是說,對象進行深拷貝要對整個對象(包括對象的引用類型)進行拷貝
- 深拷貝的實現方式:1.重寫clone方法來實現深拷貝;2.通過對象序列化實現深拷貝(推薦)
c)深拷貝應用案例
源碼:詳情
public class DeepPrototype implements Serializable,Cloneable { String name; DeepCloneTarget deepCloneTarget; //方案1:重寫clone方法 @Override protected Object clone() throws CloneNotSupportedException { Object deep = null; //完成對基本類型和String的克隆 deep = super.clone(); //對引用類型的屬性進行單獨處理 DeepPrototype deepPrototype = (DeepPrototype) deep; deepPrototype.deepCloneTarget = (DeepCloneTarget) deepCloneTarget.clone(); return deepPrototype; } //方案2:通過對昂的序列化實現(推薦) public Object deepClone() { ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; ByteArrayInputStream bis = null; ObjectInputStream ois = null; try { //序列化當前對象 bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this); //反序列化 bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); return (DeepPrototype)ois.readObject(); } catch (Exception e) { e.printStackTrace(); } finally { try { ois.close(); bis.close(); oos.close(); bos.close(); } catch (IOException e) { e.printStackTrace(); } } return null; } @Override public String toString() { return "DeepPrototype{" + "hascode=" + this.hashCode() + " , name='" + name + '\'' + ", deepCloneTarget=" + deepCloneTarget + '}'; } } class DeepCloneTarget implements Cloneable,Serializable { private static final long serialVersionUID = 1L; private String name1; private String name2; public DeepCloneTarget(String name1, String name2) { this.name1 = name1; this.name2 = name2; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "DeepCloneTarget{" + "hashcode= " + this.hashCode() + ", name1='" + name1 + '\'' + ", name2='" + name2 + '\'' + '}'; } }
public class Client { public static void main(String[] args) throws CloneNotSupportedException { DeepPrototype p = new DeepPrototype(); p.name = "宋江"; p.deepCloneTarget = new DeepCloneTarget("大牛", "小牛"); //方式 1 完成深拷貝 DeepPrototype p1 = (DeepPrototype) p.clone(); System.out.println("p..." +p); System.out.println("p1..." +p1); //方式 2 完成深拷貝 DeepPrototype p2 = (DeepPrototype) p.deepClone(); System.out.println("p2..." +p2); } }
6,原型模式注意事項
- 創建新的對象比較復雜時,可以利用原型模式簡化對象的創建過程,同時也能夠提高效率
- 並不用重復初始化對象,而是動態地或得對象運行時的狀態
- 如果原始對象發生變化(增加或者減少屬性),其他克隆對象也會發生相應的變化,無需修改代碼
- 在實現深克隆的時候推薦使用對象序列化
- 缺點:需要為每一個類配備一個克隆方法,這對全新的類來說不是很難,但對已有的類進行改造時,需要修改其源代碼,違背了ocp原則。
二、建造者模式
1,基本介紹
建造者模式(Builder Pattern)又叫生成器模式,時一種對象構建模式。它可以將復雜對象的建造過程抽象出來(抽象類別),使這個抽象過程的不同實現方法可以構造出不同表現(屬性)的對象。
建造者模式是一步一步創建一個復雜的對象,它不允許用戶只通過指定復雜對象的類型和內容就可以構建它們,用戶不需要知道內部的具體構建細節。
a)uml類圖
b)角色分析
- Product(產品角色):一個具體的產品對象
- Builder(抽象建造者):創建一個Product對象的各個部件指定的接口/抽象類。(接口和抽象類都可以做抽象層)抽象構造者只需要指定建造一個產品的流程,並不管具體的建造細節。
- ConcreteBuilder(具體建造者):實現接口/抽象類,構建和裝配各個部件。負責具體的建造細節。
- Director(指揮者):構建一個使用Builder接口的對象。它主要是用於創建一個復雜的對象。它主要有兩個作用,一是:隔離了客戶與對象的生產過程,二是:負責控制產品對象的生產過程。
2,案例分析
a)需求
- 需要建房子:這一過程為打樁、砌牆、封頂
- 房子有各種各樣的,比如普通房、高樓、別墅,各種房子的過程雖然一樣,但是要求使不同的
b)uml分析類圖
c)代碼實現
源碼:詳情
//產品 public class House { private String basic; private String wall; private String roof; ...get set } //抽象的構建者 public abstract class HouseBuilder { protected House house = new House(); abstract void builderBasic(); abstract void builderWall(); abstract void roofed(); public House build() { return house; } } //具體的構建者實現(省略HighBuilding..) public class CommonHouse extends HouseBuilder { @Override public void builderBasic() { System.out.println("普通房子打地基"); } @Override public void builderWall() { System.out.println("普通房子砌牆"); } @Override public void roofed() { System.out.println("普通房子封頂"); } } //指揮者,這里會去指定制作流程,返回產品 public class HouseDirector { HouseBuilder houseBuilder = null; public HouseDirector(HouseBuilder houseBuilder) { this.houseBuilder = houseBuilder; } public House houseBuilder() { houseBuilder.builderBasic(); houseBuilder.builderWall(); houseBuilder.roofed(); return houseBuilder.build(); } } //客戶端調用者 public class Client { public static void main(String[] args) { HouseDirector houseDirector = new HouseDirector(new CommonHouse()); House house = houseDirector.houseBuilder(); System.out.println(house + "構建完畢"); } }
3,建造者模式在JDK應用(StringBuilder)
- Appendable接口中定義了多個append方法(抽象方法),即Appendable為抽象制造者,定義了抽象方法
- AbstractStringBuilder雖然只是抽象類,但是它實現了Appendable接口的方法,因此這里的AbstractStringBuilder已經是建造者,只是不能實例化(因為它是抽象類)
- StringBuilder既充當了指揮者角色,同時也充當了具體的制造者,建造方法的實現是由AbstractStringBuilder完成,而StringBuilder繼承了AbstractStringBuilder(它重寫了AbstractStringBuilder的append方法)
4,注意事項
- 客戶端(使用程序)不必知道產品內部組成的細節(直接調用方法即可),將產品本身與產品的創建過程解耦,使得相同的創建過程可以創建不同的產品對象
- 每一個具體建造者都相互獨立,而與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者,用戶使用不同的具體建造者即可得到不同的產品對象
- 可以更加精細地控制產品的創建過程。(在指揮者里可以自行組織建造流程)將復雜產品的創建步驟分解在不同的方法中,使得創建過程更加清晰,也更方便使用程序來控制創建過程。
- 增加新的具體建造者無須修改原有類庫的代碼,指揮者類針對抽象建造者類編程,系統擴展方便,符合“開閉原則”(例如:實例中只需增加新的構造者OtherHouse集成HouseBuilder,Client就可以直接使用)
- 建造者模式所創建的產品一般具有較多的共同點,其組成部分相似,如果產品之間差異性很大,則不適合使用建造者模式,因此其使用范圍受到一定的限制
- 如果產品的內部變化復雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大,因此在這種情況下,要考慮是否選擇建造者模式。
- 抽象工廠VS 建造者模式:抽象工廠模式實現對產品家族的創建:產品家族,具有不同分類維度的產品組合,采用抽象工廠模式不需要關心構建過程,只關系什么產品由什么工廠生產即可。建造者模式是要求按照指定的藍圖建造產品,它的主要目的是通過組裝零配件而生產一個新產品。