使用Java8改造出來的模板方法真的是yyds
我們在日常開發中,經常會遇到類似的場景:當要做一件事兒的時候,這件事兒的步驟是固定好的,但是每一個步驟的具體實現方式是不一定的。
通常,遇到這種情況,我們會把所有要做的事兒抽象到一個抽象類中,並在該類中定義一個模板方法。這就是所謂的模板方法模式。
以前的模板方法
在我之前的一篇《設計模式——模板方法設計模式》文章中舉過一個例子:
當我們去銀行的營業廳辦理業務需要以下步驟:1.取號、2.辦業務、3.評價。
三個步驟中取號和評價都是固定的流程,每個人要做的事兒都是一樣的。但是辦業務這個步驟根據每個人要辦的事情不同所以需要有不同的實現。
我們可以將整個辦業務這件事兒封裝成一個抽象類:
* 模板方法設計模式的抽象類
* @author hollis
*
/
public abstract
class
AbstractBusinessHandler
{
/**
* 模板方法
*/
public final
void
execute
(
)
{
getNumber
(
)
;
handle
(
)
;
judge
(
)
;
}
/**
* 取號
* @return
*/
private
void
getNumber
(
)
{
System
.out
.
println
(
"number-00"
+ RandomUtils
.
nextInt
(
)
)
;
}
/**
* 辦理業務
*/
public abstract
void
handle
(
)
;
//抽象的辦理業務方法,由子類實現
/**
* 評價
*/
private
void
judge
(
)
{
System
.out
.
println
(
"give a praised"
)
;
}
}
我們在類中定義了一個execute類,這個類編排了getNumber、handle和judge三個方法。這就是一個
模板方法。
其中getNumber和judge都有通用的實現,只有handle方法是個抽象的,需要子類根據實際要辦的業務的內容去重寫。
有了這個抽象類和模板方法,當我們想要實現一個”存錢業務”的時候,只需要繼承該AbstractBusinessHandeler並且重寫handle方法即可:
public
class
SaveMoneyHandler
extends
AbstractBusinessHandeler
{
@Override
public
void
handle
(
)
{
System
.out
.
println
(
"save 1000"
)
;
}
}
這樣,我們在執行存錢的業務邏輯的時候,只需要呼叫 SaveMoneyHandler的execute方法即可:
public
static
void
main
(
String
[
]args
)
{
SaveMoneyHandler saveMoneyHandler
=
new
SaveMoneyHandler
(
)
;
saveMoneyHandler
.
execute
(
)
;
}
輸出結果:
number
-
00958442164
save
1000
give a praised
以上,就是一個簡單的模板方法的實現。通過使用模板方法,可以幫助我們很大程度的複用程式碼。
因為我們要在銀行辦理很多業務,所以可能需要定義很多的實現類:
//取錢業務的實現類
public
class
DrawMoneyHandler
extends
AbstractBusinessHandeler
{
@Override
public
void
handle
(
)
{
System
.out
.
println
(
"draw 1000"
)
;
}
}
//java學習交流:737251827 進入可領取學習資源及對十年開發經驗大佬提問,免費解答!
//理財業務的實現類
public
class
MoneyManageHandler
extends
AbstractBusinessHandeler
{
@Override
public
void
handle
(
)
{
System
.out
.
println
(
"money manage"
)
;
}
}
一直以來,開發者們在使用模板方法的時候基本都是像上面這個例子一樣:需要準備一個抽象類,將部分邏輯以具體方法以及具體建構函式的形式實現,然後宣告一些抽象方法來讓子類實現剩餘的邏輯。不同的子類可以以不同的方式實現這些抽象方法,從而對剩餘的邏輯有不同的實現。
但是, 有了Java 8以後,模板方法有了另外一種實現方式,不需要定義特別多的實現類了。
Java 8 的函數語言程式設計
2014年,Oracle釋出了 Java 8,在Java 8中最大的新特性就是提供了對函數語言程式設計的支援。
Java 8在
java.util.function
下面增加增加一系列的函式介面。其中主要有Consumer、Supplier、Predicate、Function等。
本文主要想要介紹一下Supplier和Consumer這兩個,使用者兩個介面,可以幫我們很好的改造模板方法。這裡只是簡單介紹下他們的用法,並不會深入展開,如果大家想要學習更多用法,可以自行google一下。
Supplier
Supplier是一個 供給型的介面,簡單點說,這就是一個返回某些值的方法。
最簡單的一個Supplier就是下面這段程式碼:
Supplier<List<String>> listSupplier = ArrayList::new;
使用Supplier表示就是:
Supplier<List<String>> listSupplier = ArrayList::new;
Consumer
Consumer 介面 消費型介面,簡單點說,這就是一個使用某些值(如方法引數)並對其進行操作的方法。
最簡單的一個Consumer就是下面這段程式碼:
public
void
sum
(
String a1
)
{
System
.out
.
println
(a1
)
;
}
使用Consumer表示就是:
Consumer<String> printConsumer = a1 -> System.out.println(a1);
Consumer的用法,最見的的例子就是是
Stream.forEach(Consumer)
這樣的用法,
它接受一個Consumer,該Consumer消費正在迭代的流中的元素,並對每個元素執行一些操作,比如列印:
Consumer
<String
> stringConsumer
=
(s
)
-
> System
.out
.
println
(s
.
length
(
)
)
;
Arrays
.
asList
(
"ab"
,
"abc"
,
"a"
,
"abcd"
)
.
stream
(
)
.
forEach
(stringConsumer
)
;
Java 8以後的模板方法
在介紹過了Java 8中的Consumer、Supplier之後,我們來看下怎麼改造之前我們介紹過的模板方法。
首先,我們定義一個BankBusinessHandler類,並且重新定義一個execute方法,這個方法有一個入參,是Consumer型別的,然後移除handle方法,重新編排後的模板方法內容如下:
/**
* @author Hollis
*/
public
class
BankBusinessHandler
{
private
void
execute
(
Consumer
<BigDecimal
> consumer
)
{
getNumber
(
)
;
consumer
.
accept
(
null
)
;
judge
(
)
;
}
private
void
getNumber
(
)
{
System
.out
.
println
(
"number-00"
+ RandomUtils
.
nextInt
(
)
)
;
}
private
void
judge
(
)
{
System
.out
.
println
(
"give a praised"
)
;
}
}
我們實現的模板方法execute中,編排了getNumber、judge以及consumer.accept,這裡面consumer.accept就是具體的業務邏輯,可能是存錢、取錢、理財等。需要由其他方法呼叫execute的時候傳入。
這時候,我們想要實現”存錢”業務的時候,需要BankBusinessHandler類中增加以下方法:
/**
* @author Hollis
*/
public
class
BankBusinessHandler
{
public
void
save
(
BigDecimal amount
)
{
execute
(a
-
> System
.out
.
println
(
"save "
+ amount
)
)
;
}
}
在save方法中,呼叫execute方法,並且在入參處傳入一個實現了”存錢”的業務邏輯的Comsumer。
這樣,我們在執行存錢的業務邏輯的時候,只需要呼叫 BankBusinessHandler的save方法即可:
public
static
void
main
(String
[
] args
) throws
{
BankBusinessHandler businessHandler
=
new
BankBusinessHandler
(
)
;
businessHandler
.
save
(
new
BigDecimal
(
"1000"
)
)
;
}
輸出結果:
number
-
001736151440
save1000
give a praised
如上,當我們想要實現取錢、理財等業務邏輯的時候,和存錢類似:
/**
* @author Hollis
*/
public
class
BankBusinessHandler
{
public
void
save
(
BigDecimal amount
)
{
execute
(a
-
> System
.out
.
println
(
"save "
+ amount
)
)
;
}
public
void
draw
(
BigDecimal amount
)
{
execute
(a
-
> System
.out
.
println
(
"draw "
+ amount
)
)
;
}
public
void
moneyManage
(
BigDecimal amount
)
{
execute
(a
-
> System
.out
.
println
(
"draw "
+ amount
)
)
;
}
}
可以看到,通過使用Java 8中的Comsumer,我們把模板方法改造了,
改造之後不再需要抽象類、抽象方法,也不再需要為每一個業務都建立一個實現類了。我們可以把所有的業務邏輯內聚在同一個業務類中。這樣非常方便這段程式碼的後期運維。
前面介紹如何使用Consumer進行改造模板方法,那麼Supplier有什麼用呢?
我們的例子中,在取號、辦業務、評價這三個步驟中,辦業務是需要根據業務情況進行定製的,所以,我們在模板方法中,把辦業務這個作為擴充套件點開放給外部。
有這樣一種情況,那就是現在我們辦業務的時候,取號的方式也不一樣,可能是到銀行網點取號、在網上取號或者銀行客戶經理預約的無需取號等。
無論取號的方式如何,最終結果都是取一個號;而取到的號的種類不同,可能接收到的具體服務也不同,比如vip號會到VIP櫃檯辦理業務等。
想要實現這樣的業務邏輯,就需要使用到Supplier, Supplier是一個”供給者”,他可以用來定製”取號邏輯”。
首先,我們需要改造下模板方法:
/**
* 模板方法
*/
protected
void
execute
(
Supplier
<String
> supplier
, Consumer
<BigDecimal
> consumer
)
{
String number
= supplier
.
get
(
)
;
System
.out
.
println
(number
)
;
if
(number
.
startsWith
(
"vip"
)
)
{
//Vip號分配到VIP櫃檯
System
.out
.
println
(
"Assign To Vip Counter"
)
;
}
else
if
(number
.
startsWith
(
"reservation"
)
)
{
//預約號分配到專屬客戶經理
System
.out
.
println
(
"Assign To Exclusive Customer Manager"
)
;
}
else
{
//預設分配到普通櫃檯
System
.out
.
println
(
"Assign To Usual Manager"
)
;
}
consumer
.
accept
(
null
)
;
judge
(
)
;
}
經過改造,execute的入參增加了一個supplier,這個supplier可以提供一個號碼。至於如何取號的,交給呼叫execute的方法來執行。
之後,我們可以定義多個存錢方法,分別是Vip存錢、預約存錢和普通存錢:
public
class
BankBusinessHandler
extends
AbstractBusinessHandler
{
//java學習交流:737251827 進入可領取學習資源及對十年開發經驗大佬提問,免費解答!
public
void
saveVip
(
BigDecimal amount
)
{
execute
(
(
)
-
>
"vipNumber-00"
+ RandomUtils
.
nextInt
(
)
, a
-
> System
.out
.
println
(
"save "
+ amount
)
)
;
}
public
void
save
(
BigDecimal amount
)
{
execute
(
(
)
-
>
"number-00"
+ RandomUtils
.
nextInt
(
)
, a
-
> System
.out
.
println
(
"save "
+ amount
)
)
;
}
public
void
saveReservation
(
BigDecimal amount
)
{
execute
(
(
)
-
>
"reservationNumber-00"
+ RandomUtils
.
nextInt
(
)
, a
-
> System
.out
.
println
(
"save "
+ amount
)
)
;
}
}
在多個不同的存錢方法中,實現不同的取號邏輯,把取號邏輯封裝在supplier中,然後傳入execute方法即可。
測試程式碼如下:
BankBusinessHandler businessHandler
=
new
BankBusinessHandler
(
)
;
businessHandler
.
saveVip
(
new
BigDecimal
(
"1000"
)
)
;
輸出結果:
vipNumber
-
001638110566
Assign To Vip Counter
save
1000
give a praised
以上,我們就是用Comsumer和Supplier改造了模板方法模式。
使用Java 8對模板方法進行改造之後,可以進一步的減少程式碼量,至少可少建立很多實現類,大大的減少重複程式碼,提升可維護性。
當然,這種做法也不是十全十美的,有一個小小的缺點,那就是 理解成本稍微高一點,對於那些對函數語言程式設計不太熟悉的開發者來說, 上手成本稍微高了一些。。。
總結
以上,我們介紹了什麼是模板方法模式,以及如何使用Comsumer和Supplier改造模板方法模式。
這樣的做法是我們日常開發中經常會用到的,其實,我覺得本文中的例子並不是完完全全能表達出來我想表達的意思,但是我們的真實業務中的邏輯講起來又比較複雜。
所以,這就需要大家能夠多多理解並且實踐一下。如果你程式碼中用到過模板方法模式,那一定是可以通過本文中的方法進行改造的。
如果你還沒用過模板方法模式,那說明你的應用中一定有很多重複程式碼,那就趕緊用起來。
作為一個開發工程師,我們要盡最大努力的消滅應用中的重複程式碼,功在當代,利在千秋!
(全文完)
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70010294/viewspace-2847675/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- innodb是如何存資料的?yyds
- Serverless:這真的是未來嗎?Server
- 《八方旅人》是如何用虛幻4引擎創造出來的?
- Serverless:這真的是未來嗎?(二)Server
- Serverless:這真的是未來嗎?(一)Server
- 使用java8的方法引用替換硬編碼Java
- 你真的知道協程是怎麼來的嗎?
- 使用Java填充Word模板的方法詳解Java
- 看來我是真的不適合華為系的。。。
- Go HTML/template 模板使用方法GoHTML
- Figure AI把「終結者」造出來了AI
- Java8——方法引用Java
- Java8 常用方法Java
- 關於熱搜「原來吃辣是有好處的」是真的嗎?
- MySQL 預設排序真的是按主鍵來排序的嗎MySql排序
- C# 中的 is 真的是越來越強大,越來越語義化C#
- ES6 -- 模板字串使用方法字串
- Java8的Stream API使用JavaAPI
- MVC 檢視不使用模板頁的兩種方法MVC
- 使用 Promise 來改寫 JavaScript 的載入邏輯PromiseJavaScript
- 使用Java8中的Optional類來消除程式碼中的null檢查JavaNull
- Java8 預設方法Java
- java8(二)方法引用Java
- IT真的是萬能的嗎?
- 【筆記/模板】線段樹(改)筆記
- java8之ForkJoin框架的使用Java框架
- Vue中使用裝飾器,我是認真的Vue
- 低程式碼 yyds
- 拓撲排序,YYDS排序
- Flask——模板的使用Flask
- 一.如何使用Keil來新建一個STM32的模板
- 這麼簡單,還不會使用java8 stream流的map()方法嗎?Java
- 當紅開發語言Go,真的是未來的技術主流嗎?Go
- 乾貨:射擊遊戲中強烈的手感如何營造出來?遊戲
- Java8 的 Stream 流式操作之王者歸來Java
- SpringAI 她來了,真的來了SpringAI
- 淺談java8中的流的使用Java
- 易優的前端模板模板是在哪個目錄?前端