Java設(shè)計(jì)模式中的建造者(Builder)模式解讀
為什么需要建造者模式
在我們?nèi)粘5拈_(kāi)發(fā)中,一般使用new 關(guān)鍵字通過(guò)構(gòu)造器就可以實(shí)現(xiàn)對(duì)象的創(chuàng)建,然后通過(guò)set來(lái)實(shí)現(xiàn)成員變量的修改, 為什么在Java中還需要建造者模式來(lái)創(chuàng)建對(duì)象?
假如我們系統(tǒng)中,需要有一個(gè)統(tǒng)一的日志模塊,包含了日志內(nèi)容(content)、日志所屬組織(orgId)、日志來(lái)源(logSource: 如app, 平臺(tái)操作)、用戶名(username)。
通常情況下我們可以直接通過(guò)定義一個(gè)日志類, 然后通過(guò)構(gòu)造器的方式, 進(jìn)行參數(shù)設(shè)置。
public class LogEntity {
@Getter
private Long id;
/**
* 操作時(shí)間
*/
@Getter
private String eventDate;
/**
* 操作日志模塊: default other
*/
@Getter
private String module;
/**
* 操作內(nèi)容
*/
@Getter
private String message;
/**
* IP
*/
@Getter
private String ipAddress;
/**
* 操作用戶
*/
@Getter
private String username;
/**
* 日志所屬組織
*/
@Getter
private Long organizationId;
/**
* 日志來(lái)源('日志來(lái)源:1:終端上報(bào),2:平臺(tái)下發(fā),3:平臺(tái)操作,4:APP操作')
*/
@Getter
private Integer logSource;
public PlatformLoggerEntity(Long id, String module, String message, Long organizationId, Integer logSource) {
this.id = id;
this.module = module;
this.message = message;
this.organizationId = organizationId;
this.logSource = logSource;
}
}現(xiàn)在日志類,只有5個(gè)參數(shù),參數(shù)的個(gè)數(shù)還在可接受范圍內(nèi),假如后續(xù)LogEntity類要進(jìn)行擴(kuò)展, 存儲(chǔ)的參數(shù)變成10個(gè)或則更多, 構(gòu)造器就會(huì)變得很長(zhǎng),并且可讀性和易用性都很變得很差。在使用構(gòu)造器時(shí),還需要記得每個(gè)位置傳遞的是什么字段,如果字段類型相同,還有可能出現(xiàn)傳遞錯(cuò)誤的情況。
你可能會(huì)說(shuō),我們可以通過(guò)set方法來(lái)設(shè)置值,確實(shí),通過(guò)set方法來(lái)設(shè)置值可以避免必填字段和非必填字段問(wèn)題。 代碼的可用性也提高了。
出現(xiàn)的問(wèn)題
1.id作為必填字段,需要寫(xiě)在構(gòu)造器中,如果必填字段過(guò)多,又會(huì)出現(xiàn)構(gòu)造器參數(shù)列表過(guò)長(zhǎng)的問(wèn)題
2. 字段間存在關(guān)聯(lián)關(guān)系,比如logSource是平臺(tái),則需要設(shè)置用戶的所屬企業(yè)id和用戶名, 如果我們通過(guò)set來(lái)設(shè)置值,那我們校驗(yàn)的邏輯就沒(méi)有地方放了
3. 如果我們想把LogEntity設(shè)置為不可變對(duì)象,對(duì)象初始化后就不允許改變,那么我們?cè)俦┞秙et方法,就達(dá)到到該效果
因此我們可以通過(guò)建造者模式重寫(xiě)方面的類, 重寫(xiě)好的類如下:
public class PlatformLoggerEntity {
/**
* 使用雪花算法生成ID
*/
@Getter
private Long id;
/**
* 操作時(shí)間
*/
@Getter
private String eventDate;
/**
* 操作日志模塊: default other
*/
@Getter
private String module;
/**
* 操作內(nèi)容
*/
@Getter
private String message;
/**
* IP
*/
@Getter
private String ipAddress;
/**
* 操作用戶
*/
@Getter
private String username;
/**
* 日志所屬組織
*/
@Getter
private Long organizationId;
/**
* 日志來(lái)源('日志來(lái)源:1:終端上報(bào),2:平臺(tái)下發(fā),3:平臺(tái)操作,4:APP操作')
*/
@Getter
private Integer logSource;
/**
* 顯示類型 0:正常顯示 1:超鏈顯示
*/
@Getter
private Integer showType;
private PlatformLoggerEntity(Builder builder) {
this.id = IdUtil.next();
this.username = SystemHelper.getCurrentUserName();
this.ipAddress = SystemHelper.getIpAddress();
this.organizationId = builder.organizationId;
this.module = builder.module.getModule();
this.message = builder.message;
this.monitoringOperation = builder.monitoringOperation;
this.monitorName = builder.monitorName;
this.plateColor = builder.plateColor;
this.showType = builder.showType;
this.eventDate = LocalDateUtils.dateTimeFormat(new Date());
this.logSource = builder.logSource.getLoggerSource();
}
/**
* @return 創(chuàng)建builder類的實(shí)例對(duì)象
*/
public static Builder newBuilder() {
return new Builder();
}
/**
* 日誌Builder類
*/
public static class Builder {
/**
* 操作日志模塊: default other
*/
private LoggerModule module;
/**
* 操作內(nèi)容
*/
private String message;
/**
* 日志所屬組織
*/
private Long organizationId;
/**
* 日志來(lái)源('日志來(lái)源:1:終端上報(bào),2:平臺(tái)下發(fā),3:平臺(tái)操作,4:APP操作')
*/
private LoggerSource logSource;
/**
* 顯示類型 0:正常顯示 1:超鏈顯示
*/
private Integer showType;
publick LogEntity build() {
// 默認(rèn)為平臺(tái)操作
if (Objects.isNull(logSource)) {
this.logSource = LoggerSource.PLATFORM_OPERATION;
}
if (Objects.isNull(organizationId)) {
this.organizationId = SystemHelper.getCurrentUserOrgId();
}
// 用于前端展示
if (Objects.isNull(module)) {
this.module = LoggerModule.DEFAULT;
this.showType = LoggerConstant.SHOW_TYPE_NORMAL;
} else if (module.equals(LoggerModule.DEFAULT)) {
this.showType = LoggerConstant.SHOW_TYPE_NORMAL;
} else {
this.showType = LoggerConstant.SHOW_TYPE_HYPERLINK;
}
return new PlatformLoggerEntity(this);
}
public Builder module(LoggerModule module) {
this.module = module;
return this;
}
public Builder message(String message) {
this.message = message;
return this;
}
public Builder organizationId(Long organizationId) {
this.organizationId = organizationId;
return this;
}
public Builder logSource(LoggerSource logSource) {
this.logSource = logSource;
return this;
}
}
}我們重寫(xiě)LogEntity類后, 我們可以通過(guò)newBuilder來(lái)獲取內(nèi)部的Builder對(duì)象,并且通過(guò)build函數(shù)來(lái)創(chuàng)建LogEntity對(duì)象。
1.從上面代碼可以看出LogEntitty所有的成員屬性都只有g(shù)et方法,避免出現(xiàn)隨意修改的情況
2.通過(guò)LogEntity內(nèi)部的Builder類中的build() 來(lái)增加了相關(guān)聯(lián)屬性的驗(yàn)證,避免調(diào)用則漏傳或則錯(cuò)傳參數(shù)
總結(jié)
什么情況下我們可以選擇建造者(Builder)模式來(lái)創(chuàng)建對(duì)象, 總結(jié)了一下三點(diǎn):
1.如果類中的必填字段過(guò)多,構(gòu)造函數(shù)過(guò)長(zhǎng),生成對(duì)象時(shí)需要校驗(yàn)必填屬性
2.如果字段之間存在關(guān)聯(lián),生成對(duì)象時(shí)進(jìn)行校驗(yàn)。
3.我們需要?jiǎng)?chuàng)建不可變對(duì)象,不能暴露實(shí)體的set方法時(shí)
到此這篇關(guān)于Java設(shè)計(jì)模式中的建造者(Builder)模式解讀的文章就介紹到這了,更多相關(guān)Java建造者模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中static和static?final的區(qū)別詳解
這篇文章主要介紹了Java中static和static?final的區(qū)別詳解,開(kāi)發(fā)時(shí)我們經(jīng)常用到static以及static?final來(lái)修飾我們的字段變量,那么他們到底有什么區(qū)別呢?其實(shí)他們的區(qū)別可以用使用字節(jié)碼文件來(lái)解析,需要的朋友可以參考下2023-10-10
淺析Java如何優(yōu)雅的設(shè)計(jì)接口狀態(tài)碼和異常
HTTP協(xié)議里定義了一系列的狀態(tài)碼用來(lái)表明請(qǐng)求的狀態(tài),如常用的200表示請(qǐng)求正常,404表示請(qǐng)求的資源不存在,所以本文就來(lái)和大家討論一下如何優(yōu)雅的設(shè)計(jì)接口狀態(tài)碼和異常,感興趣的可以了解下2024-03-03
Java 線程池_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
系統(tǒng)啟動(dòng)一個(gè)新線程的成本是比較高的,因?yàn)樗婕暗脚c操作系統(tǒng)的交互。在這種情況下,使用線程池可以很好的提供性能,尤其是當(dāng)程序中需要?jiǎng)?chuàng)建大量生存期很短暫的線程時(shí),更應(yīng)該考慮使用線程池2017-05-05
SpringBoot中定時(shí)任務(wù)@Scheduled的多線程使用詳解
這篇文章主要為大家詳細(xì)介紹了pring Boot定時(shí)任務(wù)@Scheduled的多線程原理以及如何加入線程池來(lái)處理定時(shí)任務(wù),感興趣的可以了解一下2023-04-04
Spring MVC+mybatis實(shí)現(xiàn)注冊(cè)登錄功能
這篇文章主要為大家詳細(xì)介紹了Spring MVC+mybatis實(shí)現(xiàn)注冊(cè)登錄功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07
SpringCloud中使用webclient(get和post)請(qǐng)求微服務(wù)接口數(shù)據(jù)
在SpringCloud項(xiàng)目中使用WebClient調(diào)用微服務(wù)時(shí),涉及配置WebClient、發(fā)起get和post請(qǐng)求等操作,如請(qǐng)求頭設(shè)置、服務(wù)地址配置、數(shù)據(jù)轉(zhuǎn)換處理、異常處理等,避免在循環(huán)中使用WebClient請(qǐng)求、路徑設(shè)置細(xì)節(jié)以及數(shù)據(jù)返回處理技巧,本文旨在幫助理解和應(yīng)用WebClient進(jìn)行微服務(wù)調(diào)用2024-10-10

