activexobject对象不能创建_【设计模式】建造者模式:你创建对象的方式有它丝滑吗?...
目錄
- 什么是建造者模式
- 為什么要使用建造者模式
- 構造函數創建對象
- set方式構建對象
- java實現建造者模式
- 第一種實現方式
- 第二種方式
- 建造者模式與構造函數的對比
- 建造者模式與工廠模式的對比
- 總結
什么是建造者模式
建造者模式是設計模式的一種,將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。
其實建造者模式是被翻譯過來的,他原名叫builder模式,也被稱為生成器模式,這種模式的實現非常的簡單,只是在使用方面可能會有點摸不著方向,它主要解決復雜的對象創建,比如參數過長、校驗過多等等。
為什么要使用建造者模式
我們都知道,創建對象的方法有很多,new是我們最常見也是最熟悉的一種,我們為什么不使用我們最熟悉的而選用建造者模式呢?雖然new是我們最熟悉的,但不一定是最合適的,為什么這么說呢?我們舉個例子來說明一下。
我們現在定義一個對象:ThreadConfig,ThreadConfig有5個屬性:核心線程數(corePoolSize)、最大線程數(maxPoolSize)、隊列數(queueCapacity)、空閑時間退出(keepAliveTime)、是否允許線程退出(allowCoreThreadTimeout)。屬性有必填、有選填。
屬性名必填默認值注釋threadName是
線程名corePoolSize否4核心線程數maxPoolSize是
核心線程數queueCapacity是
最大線程數keepAliveTime是
當線程空閑時間達到keepAliveTime,該線程會退出allowCoreThreadTimeout是
是否允許核心線程數空閑時退出
創建對象的時候要滿足以下要求:
1.最大線程數不傳,默認為核心線程數的大小。
2.最大線程數不能小于核心線程數。
3.如果填寫隊列數,隊列書不能小于等于0。
4.如果填寫keepAliveTime,不能小于等于0。
看到這樣的一個對象,如果是你,你會怎么設計他的對象創建呢?
構造函數創建對象
大家想到的第一種創建方式可能就是構造函數,那我們先使用構造函數實現一下這個對象的創建
package com.ymy.builder;import lombok.ToString; import org.springframework.util.StringUtils;@ToString public class ThreadConfig {/** * 核心線程默認值 */private static final Integer CORE_POOL_SIZE = 4;private String threadName;/** * 核心線程數 */private Integer corePoolSize = CORE_POOL_SIZE;/** * 最大線程數 */private Integer maxPoolSize;/** * 隊列數 */private Integer queueCapacity;/** * 當線程空閑時間達到keepAliveTime,該線程會退出 */private Integer keepAliveTime;/** * 是否允許核心線程數空閑時退出 */private boolean allowCoreThreadTimeout;public ThreadConfig(String threadName,Integer corePoolSize,Integer maxPoolSize,Integer queueCapacity,Integer keepAliveTime) throws IllegalAccessException {if(StringUtils.isEmpty(threadName)){throw new IllegalAccessException("線程名不能為空!");}this.threadName = threadName;if(null != corePoolSize ){if( corePoolSize <= 0){throw new IllegalAccessException("核心線程數不能小于等于0!");}this.corePoolSize = corePoolSize;}if(null != maxPoolSize ){if(maxPoolSize < this.corePoolSize){throw new IllegalAccessException("最大線程數不能小于核心線程數!");}this.maxPoolSize = corePoolSize;}if(null != queueCapacity ){if( queueCapacity <= 0 ){throw new IllegalAccessException("隊列書不能小于等于0!");}this.queueCapacity = queueCapacity;}if(null != keepAliveTime ){if( keepAliveTime <= 0 ){throw new IllegalAccessException("空閑時間不能小于等于0!");}this.keepAliveTime = keepAliveTime;}} }@ToString注解是lombok依賴提供的
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>創建對象
package com.ymy.builder;public class Test {public static void main(String[] args) throws IllegalAccessException {ThreadConfig config = new ThreadConfig("thread-1",5,2,5,10);System.out.println(config);} }輸出結果:
Exception in thread "main" java.lang.IllegalAccessException: 最大線程數不能小于核心線程數!at com.ymy.builder.ThreadConfig.<init>(ThreadConfig.java:56)at com.ymy.builder.Test.main(Test.java:6)我這里給出的核心線程數:5,但是最大線程數給的2,所以會拋出最大線程數不能小于核心線程數!這種方式看著很完美,但有一點很不友好,當參數過多的時候容易出錯,為什么這么說呢?你仔細看這行代碼
ThreadConfig config = new ThreadConfig("thread-1",5,2,5,10);除了第一個參數,其他參數都是int類型,看著好像沒啥大毛病,這是因為我的參數還不夠多,如果我這里有10個參數需要傳遞,并且都是int類型,這時候就會存在一個問題,參數很可能會被寫錯,比如最大線程數寫到了隊列數中,而且有時候還不會報錯,只有在項目運行的時候才會出現某種讓人摸不著頭腦的bug,所以使用構造函數創建對象的時候不太適合參數過長,不但容易出錯,而且讓接手代碼的人也頭痛,代碼可讀性比較差,當然當參數只有一兩個的時候,構造函數的創建方式還是很不錯的。
set方式構建對象
既然構造函數會導致參數錯誤以及可讀性較差,那我們能不能使用構造函數+set方法來創建對象呢?我們可以嘗試一下,由于只有線程名是必傳,所以構造函數只給定線程名,其他屬性都通過set賦值,改造一下代碼。
package com.ymy.builder;import lombok.ToString; import org.springframework.util.StringUtils;@ToString public class ThreadConfig {/** * 核心線程默認值 */private static final Integer CORE_POOL_SIZE = 4;private String threadName;/** * 核心線程數 */private Integer corePoolSize = CORE_POOL_SIZE;/** * 最大線程數 */private Integer maxPoolSize;/** * 隊列數 */private Integer queueCapacity;/** * 當線程空閑時間達到keepAliveTime,該線程會退出 */private Integer keepAliveTime;/** * 是否允許核心線程數空閑時退出 */private boolean allowCoreThreadTimeout;public ThreadConfig(String threadName) throws IllegalAccessException {if(StringUtils.isEmpty(threadName)){throw new IllegalAccessException("線程名不能為空!");}this.threadName = threadName;}public void setCorePoolSize(Integer corePoolSize) throws IllegalAccessException {if(null != corePoolSize ){if( corePoolSize <= 0){throw new IllegalAccessException("核心線程數不能小于等于0!");}this.corePoolSize = corePoolSize;}}public void setMaxPoolSize(Integer maxPoolSize) throws IllegalAccessException {if(null != maxPoolSize ){if(maxPoolSize < this.corePoolSize){throw new IllegalAccessException("最大線程數不能小于核心線程數!");}this.maxPoolSize = corePoolSize;}}public void setQueueCapacity(Integer queueCapacity) throws IllegalAccessException {if(null != queueCapacity ){if( queueCapacity <= 0 ){throw new IllegalAccessException("隊列書不能小于等于0!");}this.queueCapacity = queueCapacity;}}public void setKeepAliveTime(Integer keepAliveTime) throws IllegalAccessException {if(null != keepAliveTime ){if( keepAliveTime <= 0 ){throw new IllegalAccessException("空閑時間不能小于等于0!");}this.keepAliveTime = keepAliveTime;}}public void setAllowCoreThreadTimeout(boolean allowCoreThreadTimeout) {this.allowCoreThreadTimeout = allowCoreThreadTimeout;} }改造完ThreadConfig之后我們創建對象的方式也會發生細微的變化,由之前構造函數傳遞一堆參數變成了一個參數,加上了set方法,初始值由set給定。
package com.ymy.builder;public class Test {public static void main(String[] args) throws IllegalAccessException {ThreadConfig config = new ThreadConfig("thread-1");config.setCorePoolSize(5);config.setMaxPoolSize(10);config.setQueueCapacity(2);config.setKeepAliveTime(100);config.setAllowCoreThreadTimeout(false);System.out.println(config);} }這種對象的創建方式可以有效的防止賦值屬性錯亂的問題,因為看上去一目了然,基本上不會出錯,代碼可讀性也很強,完美的解決了將所有參數都放在構造函數的缺陷,那為什么還會出現建造者模式呢?可以仔細想一下,set方法這么完美,建造者模式還有必要嗎?我覺得建造者模式的出現并不是偶然。
我們現在稍微修改一下需求:當corePoolSize(核心線程數)被賦值的時候,最大線程數也必須要賦值,這個時候你覺得set方法還能滿足嗎?我覺得應該是滿足不了了吧,這是一種情況,還有一種情況set也是滿足不了的,那就是我希望對象初始化的時候一次性將所有的屬性都賦值,之后將不能被修改,這一點也是set做不到的,set方法就是提供給調用者的,所以調用者可以通過set隨時修改ThreadConfig的屬性,如果處理不當,可能會造成某種安全隱患,這個時候你可能又想到,把corePoolSize、maxPoolSize也放到構造函數中不就解決了corePoolSize賦值的時候maxPoolSize也一定要賦值的要求嗎,確實是能解決這個問題,如果像這樣的參數很多呢?然后又有可能出現參數傳錯導致詭異bug,所這時候建造者模式就閃亮登場了。
java實現建造者模式
第一種實現方式
既然構造函數和set方法無法滿足我們的需求,那自然會有滿足我們需求的新技術出現,按照之前的需求,線程名必填、corePoolSize(核心線程數)被賦值的時候,最大線程數也必須要賦值,我們一起使用建造模式來實現一下這個對象的創建。
package com.ymy.builder;import lombok.ToString; import org.springframework.util.StringUtils;@ToString public class ThreadConfig {/** * 核心線程默認值 */private static final Integer CORE_POOL_SIZE = 4;/** * 線程名 */private String threadName;/** * 核心線程數 */private Integer corePoolSize = CORE_POOL_SIZE;/** * 最大線程數 */private Integer maxPoolSize;/** * 隊列數 */private Integer queueCapacity;/** * 當線程空閑時間達到keepAliveTime,該線程會退出 */private Integer keepAliveTime;/** * 是否允許核心線程數空閑時退出 */private boolean allowCoreThreadTimeout;private ThreadConfig(ThreadConfig.Builder builder) {this.threadName = builder.threadName;if(null != builder.corePoolSize){this.corePoolSize = builder.corePoolSize;}this.maxPoolSize = builder.maxPoolSize;this.queueCapacity = builder.queueCapacity;this.keepAliveTime = builder.keepAliveTime;this.allowCoreThreadTimeout = builder.allowCoreThreadTimeout;}public static class Builder {/** * 線程名 */private String threadName;/** * 核心線程數 */private Integer corePoolSize ;/** * 最大線程數 */private Integer maxPoolSize;/** * 隊列數 */private Integer queueCapacity;/** * 當線程空閑時間達到keepAliveTime,該線程會退出 */private Integer keepAliveTime;/** * 是否允許核心線程數空閑時退出 */private boolean allowCoreThreadTimeout;public ThreadConfig build() throws IllegalAccessException { // 校驗邏輯放到這里來做,包括必填項校驗、依賴關系校驗、約束條件校驗等if (StringUtils.isEmpty(threadName)) {throw new IllegalAccessException("線程名不能為空");}if(corePoolSize != null && maxPoolSize == null){throw new IllegalAccessException("最大線程數必傳");}return new ThreadConfig(this);}public ThreadConfig.Builder corePoolSize(int corePoolSize) {if (corePoolSize <= 0) {throw new IllegalArgumentException("核心線程數不能小于等于0");}this.corePoolSize = corePoolSize;return this;}public ThreadConfig.Builder threadName(String threadName) {this.threadName = threadName;return this;}public ThreadConfig.Builder maxPoolSize(int maxPoolSize) {if (maxPoolSize < this.corePoolSize) {throw new IllegalArgumentException("最大線程數不能小于核心線程數");}this.maxPoolSize = maxPoolSize;return this;}public ThreadConfig.Builder queueCapacity(int queueCapacity) {if (queueCapacity <= 0) {throw new IllegalArgumentException("隊列不能小于等于0");}this.queueCapacity = queueCapacity;return this;}public ThreadConfig.Builder keepAliveTime(int keepAliveTime) {if (keepAliveTime <= 0) {throw new IllegalArgumentException("保持空閑線程可用的時間不能小于等于0");}this.keepAliveTime = keepAliveTime;return this;}public ThreadConfig.Builder allowCoreThreadTimeout(boolean allowCoreThreadTimeout) {this.allowCoreThreadTimeout = allowCoreThreadTimeout;return this;}}}我們來測試,傳入核心線程數不傳最先線程數
package com.ymy.builder;public class Test {public static void main(String[] args) throws IllegalAccessException {ThreadConfig config = new ThreadConfig.Builder().threadName("hello").corePoolSize(3).keepAliveTime(100).queueCapacity(2).allowCoreThreadTimeout(true).build();System.out.println(config);} }打印結果
Exception in thread "main" java.lang.IllegalAccessException: 最大線程數必傳at com.ymy.builder.ThreadConfig$Builder.build(ThreadConfig.java:89)at com.ymy.builder.Test.main(Test.java:12)核心線程數不傳
ThreadConfig config = new ThreadConfig.Builder().threadName("hello")//.corePoolSize(3).keepAliveTime(100).queueCapacity(2).allowCoreThreadTimeout(true).build();System.out.println(config);結果
ThreadConfig(threadName=hello, corePoolSize=4, maxPoolSize=null, queueCapacity=2, keepAliveTime=100, allowCoreThreadTimeout=true)Process finished with exit code 0這就說明已經達到了我們的預期效果,并且賦值清晰,不容易出錯,代碼的可讀性也比較高,但是也有一點是不足的,那就是ThreadConfig類中會出現冗余的數據Builder。
建造者模式的參數校驗放在了build()方法中,這樣做法的好處在于build是集中的處理參數問題,只有校驗通過之后才會給ThreadConfig對象實例化,為了對象的安全性,我們可以將ThreadConfig的構造函數設置成private,同時取消set方法,強制使用builder方式創建對象,這樣就大大的保證了對象的安全性。
第二種方式
上面那種方式看著是不是很爽,但是創建對象的時候還是需要new ThreadConfig.Builder(),我現在想直接ThreadConfig.Builder()就能創建對象,我不想看到new,能不能實現呢?相信java,他能,我們一起來改造一下代碼
package com.ymy.builder;import lombok.ToString; import org.springframework.util.StringUtils;@ToString public class ThreadConfig {/** * 核心線程默認值 */private static final Integer CORE_POOL_SIZE = 4;/** * 線程名 */private String threadName;/** * 核心線程數 */private Integer corePoolSize = CORE_POOL_SIZE;/** * 最大線程數 */private Integer maxPoolSize;/** * 隊列數 */private Integer queueCapacity;/** * 當線程空閑時間達到keepAliveTime,該線程會退出 */private Integer keepAliveTime;/** * 是否允許核心線程數空閑時退出 */private boolean allowCoreThreadTimeout;private ThreadConfig(ThreadConfig.Builder builder) {this.threadName = builder.threadName;if(null != builder.corePoolSize){this.corePoolSize = builder.corePoolSize;}this.maxPoolSize = builder.maxPoolSize;this.queueCapacity = builder.queueCapacity;this.keepAliveTime = builder.keepAliveTime;this.allowCoreThreadTimeout = builder.allowCoreThreadTimeout;}/** * 使用靜態方法替代new * @return */public static Builder builder() {return new Builder();}public static class Builder {/** * 構造函數,可以不寫 */Builder(){}/** * 線程名 */private String threadName;/** * 核心線程數 */private Integer corePoolSize ;/** * 最大線程數 */private Integer maxPoolSize;/** * 隊列數 */private Integer queueCapacity;/** * 當線程空閑時間達到keepAliveTime,該線程會退出 */private Integer keepAliveTime;/** * 是否允許核心線程數空閑時退出 */private boolean allowCoreThreadTimeout;public ThreadConfig build() throws IllegalAccessException { // 校驗邏輯放到這里來做,包括必填項校驗、依賴關系校驗、約束條件校驗等if (StringUtils.isEmpty(threadName)) {throw new IllegalAccessException("線程名不能為空");}if(corePoolSize != null && maxPoolSize == null){throw new IllegalAccessException("最大線程數必傳");}return new ThreadConfig(this);}public ThreadConfig.Builder corePoolSize(int corePoolSize) {if (corePoolSize <= 0) {throw new IllegalArgumentException("核心線程數不能小于等于0");}this.corePoolSize = corePoolSize;return this;}public ThreadConfig.Builder threadName(String threadName) {this.threadName = threadName;return this;}public ThreadConfig.Builder maxPoolSize(int maxPoolSize) {if (maxPoolSize < this.corePoolSize) {throw new IllegalArgumentException("最大線程數不能小于核心線程數");}this.maxPoolSize = maxPoolSize;return this;}public ThreadConfig.Builder queueCapacity(int queueCapacity) {if (queueCapacity <= 0) {throw new IllegalArgumentException("隊列不能小于等于0");}this.queueCapacity = queueCapacity;return this;}public ThreadConfig.Builder keepAliveTime(int keepAliveTime) {if (keepAliveTime <= 0) {throw new IllegalArgumentException("保持空閑線程可用的時間不能小于等于0");}this.keepAliveTime = keepAliveTime;return this;}public ThreadConfig.Builder allowCoreThreadTimeout(boolean allowCoreThreadTimeout) {this.allowCoreThreadTimeout = allowCoreThreadTimeout;return this;}} }其實改動很小僅僅只是加了一個static Builder builder(),使用靜態方法替代new對象,這樣我們創建對象的時候就不需要new了,請看創建對象代碼
ThreadConfig config =ThreadConfig.builder().threadName("hello")// .corePoolSize(3).keepAliveTime(100).queueCapacity(2).allowCoreThreadTimeout(true).build();System.out.println(config);是不是爽多了,看著真舒服,不過話說回來,你們看這種創建方式想不想lombok中給對象加了@Builder注解之后的創建方式?沒錯,就是一樣的,因為lombok中的@Builder就是建造者模式,只不過他的build并沒有我們這里的條件判斷,他是直接將屬性返回了。
建造者模式與構造函數的對比
**構造函數:**適用于參數較少,邏輯簡單的對象創建,對于參數過多的對象創建可能會造成參數錯亂的問題而導致詭異bug。
**建造者模式:**適用于參數較多,邏輯判斷較復雜的對象創建,可以讓代碼簡潔明了,但是對象的代碼增加了,不但增加了很多冗余字段,所以有時候表面看起來光鮮亮麗,內心卻是無比丑陋。
建造者模式與工廠模式的對比
對工廠模式還不太明白的朋友可以參考一下:工廠模式:你還在使用一堆的if/else創建對象嗎?
我們知道工廠模式主要是創建一個類型多個實現的對象,比如發送短信驗證碼的處理方式有很多種情況,每種情況的處理方式都不相同,還有就是創建對象的時候需要經過很多的判斷,這種情況下我們就可以考慮使用工廠模式來創建對象。
如果對象的職責比較單一,沒有多層含義,僅僅只是創建條件復雜,參數過多等等,使用建造者模式創建對象是首選,雖然對象中含有冗余代碼,但是對象的創建真的很絲滑。
總結
如果一個類中包含著大量的屬性,我們可以通過構造函數+set方法來進行對象創建,但對象如果包含一下幾點特性,那么我推薦使用建造者模式。
1.必填的字段很多,這樣會導致構造函數參數過長的問題。
2.如果屬性與屬性之間關聯性很強,比如設置了核心線程數就必須要設置最大線程數,這種情況下set方法是無法做到校驗的。
3.如果當前對象比較重要,我們希望對象被創建之后就不能被修改,所以這時候set方法就會被屏蔽,如果利用構造函數,又會出現字段過多問題。
當然了,我們不能為了用設計模式而用設計模式,對象一共就兩個屬性,我們也給他弄成建造者模式,這就有點大材小用,適得其反,一定要結合的實際的項目需求,不能盲目使用。
?
總結
以上是生活随笔為你收集整理的activexobject对象不能创建_【设计模式】建造者模式:你创建对象的方式有它丝滑吗?...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: kaggle房价预测特征意思_R语言实战
- 下一篇: 深交所互动平台_怡达股份股价涨跌幅偏离大