0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

高頻使用的幾種設(shè)計模式

CodeSheep ? 來源:CodeSheep ? 2023-05-08 09:57 ? 次閱讀

大家好,我是程序羊。 平時我們寫代碼呢,多數(shù)情況都是流水線式寫代碼,基本就可以實現(xiàn)業(yè)務(wù)邏輯了。那如何在寫代碼中找到樂趣呢,我覺得一個有效的方式就是:嘗試使用設(shè)計模式來優(yōu)化自己的業(yè)務(wù)代碼,以提高質(zhì)量! 所以今天我們就結(jié)合具體業(yè)務(wù)場景,來梳理一下日常工作中,高頻使用的幾種設(shè)計模式,相信對大家平時的工作會很有幫助。 文章很長,大家也可以收藏備用。f9c3d282-ed42-11ed-90ce-dac502259ad0.png工作中常用到哪些設(shè)計模式

1.策略模式

1.1 業(yè)務(wù)場景

假設(shè)有這樣的業(yè)務(wù)場景,大數(shù)據(jù)系統(tǒng)把文件推送過來,根據(jù)不同類型采取不同的解析方式。多數(shù)的小伙伴就會寫出以下的代碼:

if(type=="A"){
//按照A格式解析

}elseif(type=="B"){
//按B格式解析
}else{
//按照默認(rèn)格式解析
}

這個代碼可能會存在哪些問題呢?

  • 如果分支變多,這里的代碼就會變得臃腫,難以維護,可讀性低。
  • 如果你需要接入一種新的解析類型,那只能在原有代碼上修改。

說得專業(yè)一點的話,就是以上代碼,違背了面向?qū)ο?a target="_blank">編程開閉原則以及單一原則。

  • 開閉原則(對于擴展是開放的,但是對于修改是封閉的):增加或者刪除某個邏輯,都需要修改到原來代碼
  • 單一原則(規(guī)定一個類應(yīng)該只有一個發(fā)生變化的原因):修改任何類型的分支邏輯代碼,都需要改動當(dāng)前類的代碼。

如果你的代碼就是醬紫:有多個if...else等條件分支,并且每個條件分支,可以封裝起來替換的,我們就可以使用策略模式來優(yōu)化。

1.2 策略模式定義

策略模式定義了算法族,分別封裝起來,讓它們之間可以相互替換,此模式讓算法的變化獨立于使用算法的的客戶。這個策略模式的定義是不是有點抽象呢?那我們來看點通俗易懂的比喻:

假設(shè)你跟不同性格類型的小姐姐約會,要用不同的策略,有的請電影比較好,有的則去吃小吃效果不錯,有的去逛街買買買最合適。當(dāng)然,目的都是為了得到小姐姐的芳心,請看電影、吃小吃、逛街就是不同的策略。

策略模式針對一組算法,將每一個算法封裝到具有共同接口的獨立的類中,從而使得它們可以相互替換。

1.3 策略模式使用

策略模式怎么使用呢?醬紫實現(xiàn)的:

  • 一個接口或者抽象類,里面兩個方法(一個方法匹配類型,一個可替換的邏輯實現(xiàn)方法)
  • 不同策略的差異化實現(xiàn)(就是說,不同策略的實現(xiàn)類)
  • 使用策略模式

1.3.1 一個接口,兩個方法

publicinterfaceIFileStrategy{

//屬于哪種文件解析類型
FileTypeResolveEnumgainFileType();

//封裝的公用算法(具體的解析方法)
voidresolve(Objectobjectparam);
}

1.3.2 不同策略的差異化實現(xiàn)

A 類型策略具體實現(xiàn)

@Component
publicclassAFileResolveimplementsIFileStrategy{

@Override
publicFileTypeResolveEnumgainFileType(){
returnFileTypeResolveEnum.File_A_RESOLVE;
}

@Override
publicvoidresolve(Objectobjectparam){
logger.info("A 類型解析文件,參數(shù):{}",objectparam);
//A類型解析具體邏輯
}
}

B 類型策略具體實現(xiàn)

@Component
publicclassBFileResolveimplementsIFileStrategy{

@Override
publicFileTypeResolveEnumgainFileType(){
returnFileTypeResolveEnum.File_B_RESOLVE;
}


@Override
publicvoidresolve(Objectobjectparam){
logger.info("B 類型解析文件,參數(shù):{}",objectparam);
//B類型解析具體邏輯
}
}

默認(rèn)類型策略具體實現(xiàn)

@Component
publicclassDefaultFileResolveimplementsIFileStrategy{

@Override
publicFileTypeResolveEnumgainFileType(){
returnFileTypeResolveEnum.File_DEFAULT_RESOLVE;
}

@Override
publicvoidresolve(Objectobjectparam){
logger.info("默認(rèn)類型解析文件,參數(shù):{}",objectparam);
//默認(rèn)類型解析具體邏輯
}
}

1.3.3 使用策略模式

如何使用呢?我們借助spring的生命周期,使用ApplicationContextAware接口,把對用的策略,初始化到map里面。然后對外提供resolveFile方法即可。

@Component
publicclassStrategyUseServiceimplementsApplicationContextAware{


privateMapiFileStrategyMap=newConcurrentHashMap<>();

publicvoidresolveFile(FileTypeResolveEnumfileTypeResolveEnum,ObjectobjectParam){
IFileStrategyiFileStrategy=iFileStrategyMap.get(fileTypeResolveEnum);
if(iFileStrategy!=null){
iFileStrategy.resolve(objectParam);
}
}

//把不同策略放到map
@Override
publicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{
MaptmepMap=applicationContext.getBeansOfType(IFileStrategy.class);
tmepMap.values().forEach(strategyService->iFileStrategyMap.put(strategyService.gainFileType(),strategyService));
}
}

2. 責(zé)任鏈模式

2.1 業(yè)務(wù)場景

我們來看一個常見的業(yè)務(wù)場景,下訂單。下訂單接口,基本的邏輯,一般有參數(shù)非空校驗、安全校驗、黑名單校驗、規(guī)則攔截等等。很多伙伴會使用異常來實現(xiàn):

publicclassOrder{

publicvoidcheckNullParam(Objectparam){
//參數(shù)非空校驗
thrownewRuntimeException();
}
publicvoidcheckSecurity(){
//安全校驗
thrownewRuntimeException();
}
publicvoidcheckBackList(){
//黑名單校驗
thrownewRuntimeException();
}
publicvoidcheckRule(){
//規(guī)則攔截
thrownewRuntimeException();
}

publicstaticvoidmain(String[]args){
Orderorder=newOrder();
try{
order.checkNullParam();
order.checkSecurity();
order.checkBackList();
order2.checkRule();
System.out.println("ordersuccess");
}catch(RuntimeExceptione){
System.out.println("orderfail");
}
}
}

這段代碼使用了異常來做邏輯條件判斷,如果后續(xù)邏輯越來越復(fù)雜的話,會出現(xiàn)一些問題:如異常只能返回異常信息,不能返回更多的字段,這時候需要自定義異常類。

并且,阿里開發(fā)手冊規(guī)定:禁止用異常做邏輯判斷。

【強制】 異常不要用來做流程控制,條件控制。說明:異常設(shè)計的初衷是解決程序運行中的各種意外情況,且異常的處理效率比條件判斷方式要低很多。

如何優(yōu)化這段代碼呢?可以考慮責(zé)任鏈模式

2.2 責(zé)任鏈模式定義

當(dāng)你想要讓一個以上的對象有機會能夠處理某個請求的時候,就使用責(zé)任鏈模式。

責(zé)任鏈模式為請求創(chuàng)建了一個接收者對象的鏈。執(zhí)行鏈上有多個對象節(jié)點,每個對象節(jié)點都有機會(條件匹配)處理請求事務(wù),如果某個對象節(jié)點處理完了,就可以根據(jù)實際業(yè)務(wù)需求傳遞給下一個節(jié)點繼續(xù)處理或者返回處理完畢。這種模式給予請求的類型,對請求的發(fā)送者和接收者進行解耦。

責(zé)任鏈模式實際上是一種處理請求的模式,它讓多個處理器(對象節(jié)點)都有機會處理該請求,直到其中某個處理成功為止。責(zé)任鏈模式把多個處理器串成鏈,然后讓請求在鏈上傳遞:

f9e2a054-ed42-11ed-90ce-dac502259ad0.png責(zé)任鏈模式

打個比喻:

假設(shè)你晚上去上選修課,為了可以走點走,坐到了最后一排。來到教室,發(fā)現(xiàn)前面坐了好幾個漂亮的小姐姐,于是你找張紙條,寫上:“你好, 可以做我的女朋友嗎?如果不愿意請向前傳”。紙條就一個接一個的傳上去了,后來傳到第一排的那個妹子手上,她把紙條交給老師,聽說老師40多歲未婚...

2.3 責(zé)任鏈模式使用

責(zé)任鏈模式怎么使用呢?

  • 一個接口或者抽象類
  • 每個對象差異化處理
  • 對象鏈(數(shù)組)初始化(連起來)

2.3.1 一個接口或者抽象類

這個接口或者抽象類,需要:

  • 有一個指向責(zé)任下一個對象的屬性
  • 一個設(shè)置下一個對象的set方法
  • 給子類對象差異化實現(xiàn)的方法(如以下代碼的doFilter方法)
publicabstractclassAbstractHandler{

//責(zé)任鏈中的下一個對象
privateAbstractHandlernextHandler;

/**
*責(zé)任鏈的下一個對象
*/
publicvoidsetNextHandler(AbstractHandlernextHandler){
this.nextHandler=nextHandler;
}

/**
*具體參數(shù)攔截邏輯,給子類去實現(xiàn)
*/
publicvoidfilter(Requestrequest,Responseresponse){
doFilter(request,response);
if(getNextHandler()!=null){
getNextHandler().filter(request,response);
}
}

publicAbstractHandlergetNextHandler(){
returnnextHandler;
}

abstractvoiddoFilter(RequestfilterRequest,Responseresponse);

}

2.3.2 每個對象差異化處理

責(zé)任鏈上,每個對象的差異化處理,如本小節(jié)的業(yè)務(wù)場景,就有參數(shù)校驗對象、安全校驗對象、黑名單校驗對象、規(guī)則攔截對象

/**
*參數(shù)校驗對象
**/
@Component
@Order(1)//順序排第1,最先校驗
publicclassCheckParamFilterObjectextendsAbstractHandler{

@Override
publicvoiddoFilter(Requestrequest,Responseresponse){
System.out.println("非空參數(shù)檢查");
}
}

/**
*安全校驗對象
*/
@Component
@Order(2)//校驗順序排第2
publicclassCheckSecurityFilterObjectextendsAbstractHandler{

@Override
publicvoiddoFilter(Requestrequest,Responseresponse){
//invokeSecuritycheck
System.out.println("安全調(diào)用校驗");
}
}
/**
*黑名單校驗對象
*/
@Component
@Order(3)//校驗順序排第3
publicclassCheckBlackFilterObjectextendsAbstractHandler{

@Override
publicvoiddoFilter(Requestrequest,Responseresponse){
//invokeblacklistcheck
System.out.println("校驗黑名單");
}
}

/**
*規(guī)則攔截對象
*/
@Component
@Order(4)//校驗順序排第4
publicclassCheckRuleFilterObjectextendsAbstractHandler{

@Override
publicvoiddoFilter(Requestrequest,Responseresponse){
//checkrule
System.out.println("checkrule");
}
}

2.3.3 對象鏈連起來(初始化)&& 使用

@Component("ChainPatternDemo")
publicclassChainPatternDemo{

//自動注入各個責(zé)任鏈的對象
@Autowired
privateListabstractHandleList;

privateAbstractHandlerabstractHandler;

//spring注入后自動執(zhí)行,責(zé)任鏈的對象連接起來
@PostConstruct
publicvoidinitializeChainFilter(){

for(inti=0;iif(i==0){
abstractHandler=abstractHandleList.get(0);
}else{
AbstractHandlercurrentHander=abstractHandleList.get(i-1);
AbstractHandlernextHander=abstractHandleList.get(i);
currentHander.setNextHandler(nextHander);
}
}
}

//直接調(diào)用這個方法使用
publicResponseexec(Requestrequest,Responseresponse){
abstractHandler.filter(request,response);
returnresponse;
}

publicAbstractHandlergetAbstractHandler(){
returnabstractHandler;
}

publicvoidsetAbstractHandler(AbstractHandlerabstractHandler){
this.abstractHandler=abstractHandler;
}
}

運行結(jié)果如下:

非空參數(shù)檢查
安全調(diào)用校驗
校驗黑名單
checkrule

3. 模板方法模式

3.1 業(yè)務(wù)場景

假設(shè)我們有這么一個業(yè)務(wù)場景:內(nèi)部系統(tǒng)不同商戶,調(diào)用我們系統(tǒng)接口,去跟外部第三方系統(tǒng)交互(http方式)。走類似這么一個流程,如下:

f9fa4e5c-ed42-11ed-90ce-dac502259ad0.png

一個請求都會經(jīng)歷這幾個流程:

  • 查詢商戶信息
  • 對請求報文加簽
  • 發(fā)送http請求出去
  • 對返回的報文驗簽

這里,有的商戶可能是走代理出去的,有的是走直連。假設(shè)當(dāng)前有A,B商戶接入,不少伙伴可能這么實現(xiàn),偽代碼如下:

//商戶A處理句柄
CompanyAHandlerimplementsRequestHandler{
Resphander(req){
//查詢商戶信息
queryMerchantInfo();
//加簽
signature();
//http請求(A商戶假設(shè)走的是代理)
httpRequestbyProxy()
//驗簽
verify();
}
}
//商戶B處理句柄
CompanyBHandlerimplementsRequestHandler{
Resphander(Rreq){
//查詢商戶信息
queryMerchantInfo();
//加簽
signature();
//http請求(B商戶不走代理,直連)
httpRequestbyDirect();
//驗簽
verify();
}
}

假設(shè)新加一個C商戶接入,你需要再實現(xiàn)一套這樣的代碼。顯然,這樣代碼就重復(fù)了,一些通用的方法,卻在每一個子類都重新寫了這一方法。

如何優(yōu)化呢?可以使用模板方法模式。

3.2 模板方法模式定義

定義一個操作中的算法的骨架流程,而將一些步驟延遲到子類中,使得子類可以不改變一個算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。它的核心思想就是:定義一個操作的一系列步驟,對于某些暫時確定不下來的步驟,就留給子類去實現(xiàn),這樣不同的子類就可以定義出不同的步驟。

打個通俗的比喻:

模式舉例:追女朋友要先“牽手”,再“擁抱”,再“接吻”, 再“拍拍..額..手”。至于具體你用左手還是右手牽,無所謂,但是整個過程,定了一個流程模板,按照模板來就行。

3.3 模板方法使用

  • 一個抽象類,定義骨架流程(抽象方法放一起)
  • 確定的共同方法步驟,放到抽象類(去除抽象方法標(biāo)記)
  • 不確定的步驟,給子類去差異化實現(xiàn)

我們繼續(xù)那以上的舉例的業(yè)務(wù)流程例子,來一起用 模板方法優(yōu)化一下哈:

3.3.1 一個抽象類,定義骨架流程

因為一個個請求經(jīng)過的流程為一下步驟:

  • 查詢商戶信息
  • 對請求報文加簽
  • 發(fā)送http請求出去
  • 對返回的報文驗簽

所以我們就可以定義一個抽象類,包含請求流程的幾個方法,方法首先都定義為抽象方法哈:

/**
*抽象類定義骨架流程(查詢商戶信息,加簽,http請求,驗簽)
*/
abstractclassAbstractMerchantService{

//查詢商戶信息
abstractqueryMerchantInfo();
//加簽
abstractsignature();
//http請求
abstracthttpRequest();
//驗簽
abstractverifySinature();

}

3.3.2 確定的共同方法步驟,放到抽象類

abstractclassAbstractMerchantService{

//模板方法流程
ResphandlerTempPlate(req){
//查詢商戶信息
queryMerchantInfo();
//加簽
signature();
//http請求
httpRequest();
//驗簽
verifySinature();
}
//Http是否走代理(提供給子類實現(xiàn))
abstractbooleanisRequestByProxy();
}

3.3.3 不確定的步驟,給子類去差異化實現(xiàn)

因為是否走代理流程是不確定的,所以給子類去實現(xiàn)。

商戶A的請求實現(xiàn):

CompanyAServiceImplextendsAbstractMerchantService{
Resphander(req){
returnhandlerTempPlate(req);
}
//走h(yuǎn)ttp代理的
booleanisRequestByProxy(){
returntrue;
}

商戶B的請求實現(xiàn):

CompanyBServiceImplextendsAbstractMerchantService{
Resphander(req){
returnhandlerTempPlate(req);
}
//公司B是不走代理的
booleanisRequestByProxy(){
returnfalse;
}

4. 觀察者模式

4.1 業(yè)務(wù)場景

登陸注冊應(yīng)該是最常見的業(yè)務(wù)場景了。就拿注冊來說事,我們經(jīng)常會遇到類似的場景,就是用戶注冊成功后,我們給用戶發(fā)一條消息,又或者發(fā)個郵件等等,因此經(jīng)常有如下的代碼:

voidregister(Useruser){
insertRegisterUser(user);
sendIMMessage();
 sendEmail();
}

這塊代碼會有什么問題呢?如果產(chǎn)品又加需求:現(xiàn)在注冊成功的用戶,再給用戶發(fā)一條短信通知。于是你又得改register方法的代碼了。。。這是不是違反了開閉原則啦。

voidregister(Useruser){
insertRegisterUser(user);
sendIMMessage();
sendMobileMessage();
 sendEmail();
}

并且,如果調(diào)發(fā)短信的接口失敗了,是不是又影響到用戶注冊了?!這時候,是不是得加個異步方法給通知消息才好。。。

實際上,我們可以使用觀察者模式優(yōu)化。

4.2 觀察者模式定義

觀察者模式定義對象間的一種一對多的依賴關(guān)系,當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴于它的對象都得到通知并被完成業(yè)務(wù)的更新。

觀察者模式屬于行為模式,一個對象(被觀察者)的狀態(tài)發(fā)生改變,所有的依賴對象(觀察者對象)都將得到通知,進行廣播通知。它的主要成員就是觀察者和被觀察者

  • 被觀察者(Observerable):目標(biāo)對象,狀態(tài)發(fā)生變化時,將通知所有的觀察者。
  • 觀察者(observer):接受被觀察者的狀態(tài)變化通知,執(zhí)行預(yù)先定義的業(yè)務(wù)。

使用場景:完成某件事情后,異步通知場景。如,登陸成功,發(fā)個IM消息等等。

4.3 觀察者模式使用

觀察者模式實現(xiàn)的話,還是比較簡單的。

  • 一個被觀察者的類Observerable ;
  • 多個觀察者Observer ;
  • 觀察者的差異化實現(xiàn)
  • 經(jīng)典觀察者模式封裝:EventBus實戰(zhàn)

4.3.1 一個被觀察者的類Observerable 和 多個觀察者Observer

publicclassObserverable{

privateListobservers
=newArrayList();
privateintstate;

publicintgetState(){
returnstate;
}

publicvoidsetState(intstate){
notifyAllObservers();
}

//添加觀察者
publicvoidaddServer(Observerobserver){
observers.add(observer);
}

//移除觀察者
publicvoidremoveServer(Observerobserver){
observers.remove(observer);
}
//通知
publicvoidnotifyAllObservers(intstate){
if(state!=1){
System.out.println(“不是通知的狀態(tài)”);
return;
}

for(Observerobserver:observers){
observer.doEvent();
}
}
}

4.3.2 觀察者的差異化實現(xiàn)

//觀察者
interfaceObserver{
voiddoEvent();
}
//Im消息
IMMessageObserverimplementsObserver{
voiddoEvent(){
System.out.println("發(fā)送IM消息");
}
}

//手機短信
MobileNoObserverimplementsObserver{
voiddoEvent(){
System.out.println("發(fā)送短信消息");
}
}
//EmailNo
EmailObserverimplementsObserver{
voiddoEvent(){
System.out.println("發(fā)送email消息");
}
}

4.3.3 EventBus實戰(zhàn)

自己搞一套觀察者模式的代碼,還是有點小麻煩。實際上,Guava EventBus就封裝好了,它 提供一套基于注解的事件總線,api可以靈活的使用,爽歪歪。

我們來看下EventBus的實戰(zhàn)代碼哈,首先可以聲明一個EventBusCenter類,它類似于以上被觀察者那種角色Observerable。

publicclassEventBusCenter{

privatestaticEventBuseventBus=newEventBus();

privateEventBusCenter(){
}

publicstaticEventBusgetInstance(){
returneventBus;
}
//添加觀察者
publicstaticvoidregister(Objectobj){
eventBus.register(obj);
}
//移除觀察者
publicstaticvoidunregister(Objectobj){
eventBus.unregister(obj);
}
//把消息推給觀察者
publicstaticvoidpost(Objectobj){
eventBus.post(obj);
}
}

然后再聲明觀察者EventListener

publicclassEventListener{

@Subscribe//加了訂閱,這里標(biāo)記這個方法是事件處理方法
publicvoidhandle(NotifyEventnotifyEvent){
System.out.println("發(fā)送IM消息"+notifyEvent.getImNo());
System.out.println("發(fā)送短信消息"+notifyEvent.getMobileNo());
System.out.println("發(fā)送Email消息"+notifyEvent.getEmailNo());
}
}

//通知事件類
publicclassNotifyEvent{

privateStringmobileNo;

privateStringemailNo;

privateStringimNo;

publicNotifyEvent(StringmobileNo,StringemailNo,StringimNo){
this.mobileNo=mobileNo;
this.emailNo=emailNo;
this.imNo=imNo;
}
}

使用demo測試:

publicclassEventBusDemoTest{

publicstaticvoidmain(String[]args){

EventListenereventListener=newEventListener();
EventBusCenter.register(eventListener);
EventBusCenter.post(newNotifyEvent("13372817283","123@qq.com","666"));
}
}

運行結(jié)果:

發(fā)送IM消息666
發(fā)送短信消息13372817283
發(fā)送Email消息123@qq.com

5. 工廠模式

5.1 業(yè)務(wù)場景

工廠模式一般配合策略模式一起使用。用來去優(yōu)化大量的if...else...switch...case...條件語句。

我們就取第一小節(jié)中策略模式那個例子吧。根據(jù)不同的文件解析類型,創(chuàng)建不同的解析對象


IFileStrategygetFileStrategy(FileTypeResolveEnumfileType){
IFileStrategyfileStrategy;
if(fileType=FileTypeResolveEnum.File_A_RESOLVE){
fileStrategy=newAFileResolve();
}elseif(fileType=FileTypeResolveEnum.File_A_RESOLV){
fileStrategy=newBFileResolve();
}else{
fileStrategy=newDefaultFileResolve();
}
returnfileStrategy;
}

其實這就是工廠模式,定義一個創(chuàng)建對象的接口,讓其子類自己決定實例化哪一個工廠類,工廠模式使其創(chuàng)建過程延遲到子類進行。

策略模式的例子,沒有使用上一段代碼,而是借助spring的特性,搞了一個工廠模式,哈哈,小伙伴們可以回去那個例子細(xì)品一下,我把代碼再搬下來,小伙伴們再品一下吧:

@Component
publicclassStrategyUseServiceimplementsApplicationContextAware{

privateMapiFileStrategyMap=newConcurrentHashMap<>();

//把所有的文件類型解析的對象,放到map,需要使用時,信手拈來即可。這就是工廠模式的一種體現(xiàn)啦
@Override
publicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{
MaptmepMap=applicationContext.getBeansOfType(IFileStrategy.class);
tmepMap.values().forEach(strategyService->iFileStrategyMap.put(strategyService.gainFileType(),strategyService));
}
}

5.2 使用工廠模式

定義工廠模式也是比較簡單的:

  • 一個工廠接口,提供一個創(chuàng)建不同對象的方法。
  • 其子類實現(xiàn)工廠接口,構(gòu)造不同對象
  • 使用工廠模式

5.3.1 一個工廠接口

interfaceIFileResolveFactory{
voidresolve();
}

5.3.2 不同子類實現(xiàn)工廠接口

classAFileResolveimplementsIFileResolveFactory{
voidresolve(){
System.out.println("文件A類型解析");
}
}

classBFileResolveimplementsIFileResolveFactory{
voidresolve(){
System.out.println("文件B類型解析");
}
}

classDefaultFileResolveimplementsIFileResolveFactory{
voidresolve(){
System.out.println("默認(rèn)文件類型解析");
}
}

5.3.3 使用工廠模式

//構(gòu)造不同的工廠對象
IFileResolveFactoryfileResolveFactory;
if(fileType=“A”){
fileResolveFactory=newAFileResolve();
}elseif(fileType=“B”){
fileResolveFactory=newBFileResolve();
}else{
fileResolveFactory=newDefaultFileResolve();
}

fileResolveFactory.resolve();

一般情況下,對于工廠模式,你不會看到以上的代碼。工廠模式會跟配合其他設(shè)計模式如策略模式一起出現(xiàn)的。

6. 單例模式

6.1 業(yè)務(wù)場景

單例模式,保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。I/O與數(shù)據(jù)庫的連接,一般就用單例模式實現(xiàn)de的。Windows里面的Task Manager(任務(wù)管理器)也是很典型的單例模式。

來看一個單例模式的例子

publicclassLanHanSingleton{

privatestaticLanHanSingletoninstance;

privateLanHanSingleton(){

}

publicstaticLanHanSingletongetInstance(){
if(instance==null){
instance=newLanHanSingleton();
}
returninstance;
}

}

以上的例子,就是懶漢式的單例實現(xiàn)。實例在需要用到的時候,才去創(chuàng)建,就比較懶。如果有則返回,沒有則新建,需要加下synchronized關(guān)鍵字,要不然可能存在線性安全問題。

6.2 單例模式的經(jīng)典寫法

其實單例模式還有有好幾種實現(xiàn)方式,如餓漢模式,雙重校驗鎖,靜態(tài)內(nèi)部類,枚舉等實現(xiàn)方式。

6.2.1 餓漢模式

publicclassEHanSingleton{

privatestaticEHanSingletoninstance=newEHanSingleton();

privateEHanSingleton(){
}

publicstaticEHanSingletongetInstance(){
returninstance;
}

}

餓漢模式,它比較饑餓、比較勤奮,實例在初始化的時候就已經(jīng)建好了,不管你后面有沒有用到,都先新建好實例再說。這個就沒有線程安全的問題,但是呢,浪費內(nèi)存空間呀。

6.2.2 雙重校驗鎖

publicclassDoubleCheckSingleton{

privatevolatile staticDoubleCheckSingletoninstance;

privateDoubleCheckSingleton(){}

publicstaticDoubleCheckSingletongetInstance(){
if(instance==null){
synchronized(DoubleCheckSingleton.class){
if(instance==null){
instance=newDoubleCheckSingleton();
}
}
}
returninstance;
}
}

雙重校驗鎖實現(xiàn)的單例模式,綜合了懶漢式和餓漢式兩者的優(yōu)缺點。以上代碼例子中,在synchronized關(guān)鍵字內(nèi)外都加了一層 if條件判斷,這樣既保證了線程安全,又比直接上鎖提高了執(zhí)行效率,還節(jié)省了內(nèi)存空間。

6.2.3 靜態(tài)內(nèi)部類

publicclassInnerClassSingleton{

privatestaticclassInnerClassSingletonHolder{
privatestaticfinalInnerClassSingletonINSTANCE=newInnerClassSingleton();
}

privateInnerClassSingleton(){}

publicstaticfinalInnerClassSingletongetInstance(){
returnInnerClassSingletonHolder.INSTANCE;
}
}

靜態(tài)內(nèi)部類的實現(xiàn)方式,效果有點類似雙重校驗鎖。但這種方式只適用于靜態(tài)域場景,雙重校驗鎖方式可在實例域需要延遲初始化時使用。

6.2.4 枚舉

publicenumSingletonEnum{

INSTANCE;
publicSingletonEnumgetInstance(){
returnINSTANCE;
}
}

枚舉實現(xiàn)的單例,代碼簡潔清晰。并且它還自動支持序列化機制,絕對防止多次實例化。

好了,以上就是今天的內(nèi)容分享,感謝大家的收看,我們下期見。


審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 算法
    +關(guān)注

    關(guān)注

    23

    文章

    4612

    瀏覽量

    92884
  • 邏輯
    +關(guān)注

    關(guān)注

    2

    文章

    833

    瀏覽量

    29469
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4788

    瀏覽量

    68603

原文標(biāo)題:看了我最常用的6種代碼技巧,同事也開始悄悄模仿了...

文章出處:【微信號:CodeSheep,微信公眾號:CodeSheep】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    液晶的幾種模式的工作原理

    液晶的幾種模式的工作原理
    發(fā)表于 08-17 21:32

    藍(lán)牙設(shè)備的幾種模式

    `藍(lán)牙設(shè)備的幾種模式1.藍(lán)牙的設(shè)備的幾種模式,沒搞懂,比如從機模式,開機后等待手機或者其他的終端設(shè)備連接。那從機
    發(fā)表于 05-18 15:42

    伺服電機的幾種常用模式及控制模式的設(shè)置相關(guān)資料分享

    伺服電機的幾種常用模式及控制模式的設(shè)置(DS402):https://miracle.blog.csdn.net/article/details/107808610DS402幾種位置
    發(fā)表于 06-28 06:54

    伺服電機的幾種常用模式及控制模式的設(shè)置是什么?

    伺服電機的幾種常用模式及控制模式的設(shè)置是什么?DS402幾種位置模式有什么區(qū)別?
    發(fā)表于 06-28 09:36

    求大神分享伺服電機的幾種常用模式及控制模式的設(shè)置

    求大神分享伺服電機的幾種常用模式及控制模式的設(shè)置
    發(fā)表于 09-27 09:01

    SPI的幾種模式不通用嗎?

    SPI的幾種模式不通用么
    發(fā)表于 10-10 08:15

    液晶的幾種模式的工作原理

    液晶的幾種模式的工作原理   1、液晶材料是液晶顯示器件的主體。無論哪一種液晶顯示器都是以下述原理為基礎(chǔ)進行工作
    發(fā)表于 10-29 21:53 ?3946次閱讀
    液晶的<b class='flag-5'>幾種</b><b class='flag-5'>模式</b>的工作原理

    幾種常見振蕩器的高頻電路

    幾種常見振蕩器的高頻電路 圖 4-7是一些常見振蕩器的高頻電路
    發(fā)表于 06-29 13:50 ?8398次閱讀
    <b class='flag-5'>幾種</b>常見振蕩器的<b class='flag-5'>高頻</b>電路

    IGBT及其子器件的幾種失效模式

    IGBT及其子器件的幾種失效模式
    發(fā)表于 02-22 10:50 ?947次閱讀
    IGBT及其子器件的<b class='flag-5'>幾種</b>失效<b class='flag-5'>模式</b>

    LTE的幾種傳輸模式介紹

    本文介紹LTE網(wǎng)絡(luò)幾種常見的傳輸模式。如:單天線端口傳輸模式、發(fā)送分集模式、開環(huán)空間分集、閉環(huán)空間分集模式、MU-MIMO傳輸
    發(fā)表于 01-09 11:42 ?1.1w次閱讀

    常用的幾種電源芯片控制模式解析

    市場上可以買到的微功率電源芯片有以下幾種控制模式:PFM、PWM 、chargepump、FPWM、PFM/PWM以及pulse-skipPWM、digitalPWM其中常見的有PFM、PWM、chargepump以及PFM/PWM。
    發(fā)表于 03-20 15:44 ?1.1w次閱讀

    詳解STM32單片機I/O的幾種工作模式

    最近有個朋友在設(shè)計低功耗設(shè)備,用的是STM32的主控,他知道我做過很多類似的超低功耗項目,于是向我咨詢了一些問題,其中就包括I/O口的幾種工作模式。今天我就詳細(xì)的來總結(jié)一下這幾種工作模式
    的頭像 發(fā)表于 05-03 18:22 ?6592次閱讀
    詳解STM32單片機I/O的<b class='flag-5'>幾種</b>工作<b class='flag-5'>模式</b>

    幾種不同的物聯(lián)網(wǎng)控制APP模式

    本文就簡單介紹當(dāng)前幾種物聯(lián)網(wǎng)控制APP模式,讓大家了解幾種不同的技術(shù)路線。
    的頭像 發(fā)表于 04-10 10:58 ?4567次閱讀
    <b class='flag-5'>幾種</b>不同的物聯(lián)網(wǎng)控制APP<b class='flag-5'>模式</b>

    cisco交換機幾種配置模式之間如何切換?

    cisco交換機幾種配置模式之間如何切換? 首先,我們需要了解幾種常見的Cisco交換機配置模式和它們各自的作用。 1. 用戶模式(User
    的頭像 發(fā)表于 10-13 17:40 ?7008次閱讀

    直流電子負(fù)載的幾種工作模式原理詳解

    直流電子負(fù)載的幾種工作模式原理詳解? 直流電子負(fù)載是一種測試電源能力的設(shè)備,其工作原理是通過將一個可控的電阻與負(fù)載相連,使負(fù)載產(chǎn)生特定的電壓和電流,并且通過該電壓和電流測量其性能的能力。直流電
    的頭像 發(fā)表于 10-26 11:38 ?1861次閱讀