java大話之創(chuàng)建型設(shè)計模式教程示例
前言
本文針對一些基礎(chǔ)的知識進(jìn)行一下總結(jié)。
創(chuàng)建型模式相對其它兩種模式也比較簡單,用的地方也會更多,理解起來也會相對更容易,所以可以放在一起一次性講完。
1. 原型模式
沒想到吧,我不從單例開始講,當(dāng)然要從最簡單的開始講,原型模式是干嘛的呢?通俗的講就是拷貝
我已經(jīng)有一個對象了,我要改這個對象的參數(shù),但是我又不能直接改,因為其它地方也引用到了這個對象,我直接改的話其它地方的引用就有問題,那我只能創(chuàng)建一個一模一樣的對象,然后在修改。那怎么做呢,總不能創(chuàng)建一個對象然后根據(jù)前一個對象一個一個屬性賦值吧?這時候就需要用原型模式了
原型模式在java中的實現(xiàn)是繼承Cloneable接口然后實現(xiàn)clone()方法
public class Test implements Cloneable{ public String name; public int age; public Object testObj; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
調(diào)用時
Test testA = new Test(); try { Test testB = (Test) testA.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); }
看得出很簡單,也很容易理解,也很常用。
除此之外,它還有一個比較重要的知識點就是深拷貝和淺拷貝
有什么區(qū)別呢?簡單來說就是內(nèi)部的對象參數(shù)也是拷貝的對象,還是指向同一個地址的同一個對象。
上面的Demo就是淺拷貝,要實現(xiàn)深拷貝的話就要自己實現(xiàn)clone方法的具體操作,比如(偽代碼,大概能看懂就行)
public class Test implements Cloneable { public String name; public int age; public Object testObj; @Override protected Object clone() throws CloneNotSupportedException { Test testB = (Test) super.clone(); Object newObj = new Object(); newObj.xxx = testObj.xxx; testB.testObj = newObj; return testB; } }
2. 建造者模式
又稱為builder模式,主要使用場景是一個對象有多個參數(shù)的時候,而且這些參數(shù)可傳可不傳,不傳的話就用默認(rèn)值。
這種情況下,如果你用構(gòu)造參數(shù)來做,你不知道要寫多少個構(gòu)造參數(shù),你用set方法來做,你就要寫很多行set操作,而且不美觀。這時候就需要用到builder模式,它是基于一個鏈?zhǔn)秸{(diào)用的方法,寫起來很美觀。
public class Test { public Test(Builder builder) { } public static class Builder { private Context context; private String name = ""; private int age = 18; private int sex = 0; private String phone = ""; public Builder(Context context) { this.context = context; } public Builder setName(String name) { this.name = name; return this; } public Builder setAge(int age) { this.age = age; return this; } public Builder setSex(int sex) { this.sex = sex; return this; } public Builder setPhone(String phone) { this.phone = phone; return this; } public Test build() { return new Test(this); } } }
大概這樣,然后調(diào)用的時候
Test testA = new Test.Builder(this) .setName("name") .setAge(16) .setSex(1) .setPhone("phone") .build();
這樣的鏈?zhǔn)秸{(diào)用看起來就比較美觀,如果你只想傳部分參數(shù)
Test testA = new Test.Builder(this) .setName("name") .build();
看得出很簡單,也很容易理解,也很常用。
實用?嗯...... 準(zhǔn)確來說,應(yīng)該是對java來說比較實用,但如果你是用kotlin的話,用data更爽。
我個人還有個習(xí)慣是如果參數(shù)少于5個,我都是用構(gòu)造方法來做,如果超過4個,我才考慮用Builder
3. 工廠模式
工廠模式簡單來說就算抽象一個工廠,你創(chuàng)建對象的操作就交給這個工廠來處理。使用的場景也比較廣泛,我個人使用比較多的場景是和業(yè)務(wù)邏輯掛鉤的,受業(yè)務(wù)的影響導(dǎo)致有些對象我沒辦法復(fù)用,不同的業(yè)務(wù)分支要創(chuàng)建不同的業(yè)務(wù)對象,我就會用工廠來管理創(chuàng)建的邏輯,這樣以后要改或者怎樣的時候去改工廠這個對象就行,還是很方便的。
工廠模式又分為幾種,簡單工廠、工廠方法和抽象工廠,我這里只講簡單工廠。為什么呢?因為你要知道當(dāng)簡單工廠的創(chuàng)建邏輯超復(fù)雜導(dǎo)致很臃腫的時候(或在用我的話來說創(chuàng)建的邏輯有多個維度的時候),會用到工廠方法和抽象工廠,可以簡單理解成他們是簡單工廠的升級版本。
我這里先拿個別人的Demo來舉個例子
public interface Shape { void draw(); }
public class Rectangle implements Shape { @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } }
public class Circle implements Shape { @Override public void draw() { System.out.println("Inside Circle::draw() method."); } }
定義了這些對象,然后寫個工廠來管理創(chuàng)建的流程
public class ShapeFactory { public Shape getShape(String shapeType){ if(shapeType == null){ return null; } if(shapeType.equalsIgnoreCase("CIRCLE")){ return new Circle(); } else if(shapeType.equalsIgnoreCase("RECTANGLE")){ return new Rectangle(); } return null; } }
重點在于ShapeFactory這個類,有這么一個工廠類的概念,工廠是一個抽象,工廠里面提供相對應(yīng)的方法來寫創(chuàng)建的邏輯。
工廠基本不會復(fù)用,因為它里面的創(chuàng)建邏輯都是和業(yè)務(wù)邏輯相關(guān)的,所以在復(fù)雜的情況中,不光會只寫if-else或者switch。那這里我可以拋出一個問題,如果這個工廠創(chuàng)建對象時,有些對象是同步創(chuàng)建,有些對象是異步創(chuàng)建,你會怎么處理?
4. 單例模式
什么時候會使用到單例,簡單來說就是全局共用一個對象的時候,那么明顯可以看出它的生命周期很長,所以很容易知道它有個缺點,很容易造成內(nèi)存泄露 ,比如你在這個單例里面存了很多全局變量又不釋放,那就會內(nèi)存泄露。
單例又分為懶漢模式和俄漢模式
// 懶漢 public class Test { private Test(){} private static Test instance = null; public static Test getInstance(){ if (instance == null){ instance = new Test(); } return instance; } }
// 餓漢 public class Test { private Test(){} private static final Test instance = new Test(); public static Test getInstance(){ return instance; } }
區(qū)別是餓漢在類加載的時候?qū)ο缶蜁?chuàng)建出來,懶漢是在第一次調(diào)用的時候?qū)ο蟊粍?chuàng)建出來。餓漢是線程安全的,懶漢是線程不安全的,所以我們一般在多線程的環(huán)境中使用懶漢的話要通過一些方式保證線程安全。
這里有兩種方法來保證線程安全,雙重檢鎖和靜態(tài)內(nèi)部類。
// 雙重檢鎖 public class Test { private static volatile Test mTest = null; private Test() { } public static Test getInstance() { if (mTest == null) { synchronized (Test.class) { if (mTest == null) { mTest = new Test(); } } } return mTest; } }
相信都能一眼看懂什么意思,唯一注意的是要加volatile來保證有序性,因為new Test()不是一個原子性操作。
// 靜態(tài)內(nèi)部類 public class Test { private static class LazyHolder{ private static final Test INSTANCE = new Test(); } private Test(){} public static final Test getInstance(){ return LazyHolder.INSTANCE; } }
這是通過使用類加載機(jī)制來保證對象只創(chuàng)建一次從而保證線程安全。 這兩種方法都可以保證線程安全,兩種方法都可以使用,但我更傾向于雙檢鎖,因為能省去一步類加載,雖然這個影響不大。
5. 總結(jié)
創(chuàng)建型模式主要是為了解決創(chuàng)建對象時的場景而被設(shè)計出來的,原型模式是為了處理復(fù)制對象的情況,建造者模式是為了處理創(chuàng)建對象時靈活傳參的情況,工廠模式是為了處理創(chuàng)建對象的邏輯,單例模式是為了處理全局共用一個對象的情況。
相信看到這里相信都能了解創(chuàng)建型模式的概念和其中各個模式的使用。好了,正文開始,我的總結(jié)主要是想做一個反推,這些東西是怎么被設(shè)計出來的,無非就是同個場景的代碼敲多了,然后進(jìn)行總結(jié),設(shè)計出一套東西來方便這個場景的開發(fā)。
比如想象一下沒有單例時會怎么去實現(xiàn)單例的功能。(或者說怎么使用low一點的方法來實現(xiàn)單例效果),如果你讓我來做,我可能會想到通過序列化來實現(xiàn),當(dāng)需要多個地方改同一個對象的某個屬性時(不考慮多線程的情況),把這個對象序列化本地,反序列化改屬性之后再序列化回去,然后把這套東西封裝起來,這也能實現(xiàn)單例的效果,但是這樣一比就會發(fā)現(xiàn)單例的解決方案比這個方便得多,所以才需要學(xué)習(xí)設(shè)計模式。
以上就是java大話之創(chuàng)建型設(shè)計模式教程示例的詳細(xì)內(nèi)容,更多關(guān)于java創(chuàng)建型設(shè)計模式 的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java參數(shù)校驗詳解之使用@Valid注解和自定義注解進(jìn)行參數(shù)驗證
在后端開發(fā)中,參數(shù)校驗是非常普遍的,下面這篇文章主要給大家介紹了關(guān)于Java參數(shù)校驗詳解之使用@Valid注解和自定義注解進(jìn)行參數(shù)驗證的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-06-06如何使用JDBC連接數(shù)據(jù)庫并執(zhí)行SQL語句
JDBC是Java數(shù)據(jù)庫連接的縮寫,是Java程序與數(shù)據(jù)庫進(jìn)行交互的標(biāo)準(zhǔn)API。JDBC主要包括Java.sql和javax.sql兩個包,通過DriverManager獲取數(shù)據(jù)庫連接對象Connection,并通過Statement或PreparedStatement執(zhí)行SQL語句2023-04-04log4j2 RollingRandomAccessFile配置過程
這篇文章主要介紹了log4j2 RollingRandomAccessFile配置過程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07Spring Cloud Stream微服務(wù)消息框架原理及實例解析
這篇文章主要介紹了Spring Cloud Stream微服務(wù)消息框架原理及實例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-06-06Spring實戰(zhàn)之獲取其他Bean的屬性值操作示例
這篇文章主要介紹了Spring實戰(zhàn)之獲取其他Bean的屬性值操作,結(jié)合實例形式分析了Spring操作Bean屬性值的相關(guān)配置與實現(xiàn)技巧,需要的朋友可以參考下2019-12-12javascript與jsp發(fā)送請求到servlet的幾種方式實例
本文分別給出了javascript發(fā)送請求到servlet的5種方式實例與 jsp發(fā)送請求到servlet的6種方式實例2018-03-03