你在公司,寫(xiě)的代碼像坨*,你咋不用它 !!!
大多數(shù)時(shí)候我都是寫(xiě)一些業(yè)務(wù)代碼,可能一堆CRUD就能解決問(wèn)題,但是這樣的工作對(duì)技術(shù)人的提升并不多,如何讓自己從業(yè)務(wù)中解脫出來(lái)找到寫(xiě)代碼的樂(lè)趣呢,我做過(guò)一些嘗試,使用設(shè)計(jì)模式改善自己的業(yè)務(wù)代碼就是其中的一種。
責(zé)任鏈設(shè)計(jì)模式
定義
請(qǐng)求在一個(gè)鏈條上處理,鏈條上的受理者處理完畢之后決定是繼續(xù)往后傳遞還是中斷當(dāng)前處理流程。
適用場(chǎng)景
適用于多節(jié)點(diǎn)的流程處理,每個(gè)節(jié)點(diǎn)完成各自負(fù)責(zé)的部分,節(jié)點(diǎn)之間不知道彼此的存在,比如OA的審批流,java web開(kāi)發(fā)中的Filter機(jī)制。舉一個(gè)生活中的例子,筆者之前租房的時(shí)候遇到了所謂的黑中介,租的時(shí)候感覺(jué)自己是上帝,但是壞了東西找他修的時(shí)候就像個(gè)孫子一樣,中介讓我找門店客服,門店客服又讓我找房東,房東又讓我找她家老公,最終好說(shuō)歹說(shuō)才把這事了了(租房一定要找正規(guī)中介)。
實(shí)踐經(jīng)驗(yàn)
筆者目前所做的業(yè)務(wù)是校園團(tuán)餐的聚合支付,業(yè)務(wù)流程很簡(jiǎn)單,1.學(xué)生打開(kāi)手機(jī)付款碼支付,2.食堂大媽使用機(jī)具掃付款碼收款。大學(xué)食堂有個(gè)背景是這樣的,食堂有補(bǔ)貼,菜品比較便宜,所以學(xué)校是不愿意讓社會(huì)人士去學(xué)校食堂消費(fèi)的,鑒于此,我們?cè)谥Ц吨凹恿艘惶资欠裨试S支付的檢驗(yàn)邏輯,大體如下:?
1.某檔口只允許某類用戶用戶消費(fèi),比如教師檔口只允許教師消費(fèi),學(xué)生檔口不允許校外用戶消費(fèi);
針對(duì)這幾類情況我建立了三類過(guò)濾器,分別是:
SpecificCardUserConsumeLimitFilter:按用戶類型判斷是否允許消費(fèi)?
DayConsumeTimesConsumeLimitFilter:按日消費(fèi)次數(shù)判斷是否允許消費(fèi)?
MuslimConsumeLimitFilter:非清真用戶是否允許消費(fèi)?
public?boolean?canConsume(String?uid,String?shopId,String?supplierId){
????//獲取用戶信息,用戶信息包含類型(student:學(xué)生,teacher:老師,unknown:未知用戶)、名族(han:漢族,mg:蒙古族)
????UserInfo?userInfo?=?getUserInfo(uid);
????//獲取消費(fèi)限制信息,限制信息包含是否允許非清真消費(fèi)、每種類型的用戶是否允許消費(fèi)以及允許消費(fèi)的次數(shù)
???ConsumeConfigInfo?consumeConfigInfo?=?getConsumeConfigInfo(shopId,supplierId)?
????//?構(gòu)造消費(fèi)限制過(guò)濾器鏈條
????ConsumeLimitFilterChain?filterChain?=?new?ConsumeLimitFilterChain();
????filterChain.addFilter(new?SpecificCardUserConsumeLimitFilter());
????filterChain.addFilter(new?DayConsumeTimesConsumeLimitFilter());
????filterChain.addFilter(new?MuslimConsumeLimitFilter());
????boolean?checkResult?=?filterChain.doFilter(filterChain,?schoolMemberInfo,?consumeConfigInfo);
????//filterChain.doFilter方法
???public?boolean?doFilter(ConsumeLimitFilterChain?filterChain,UserInfo?userInfo,
????ConsumeConfigInfo?consumeConfigInfo?){
??//迭代調(diào)用過(guò)濾器
??if(index??????return?filters.get(index++).doFilter(filterChain,?userInfo,?consumeConfigInfo);
??}
????}
????//SpecificCardUserConsumeLimitFilter.doFilter方法
?????public?boolean?doFilter(ConsumeLimitFilterChain?filterChain,UserInfo?userInfo,
????ConsumeConfigInfo?consumeConfigInfo?){
????????????????//獲取某一類型的消費(fèi)限制,比如student允許消費(fèi),unknown不允許消費(fèi)
??CardConsumeConfig?cardConsumeConfig?=?findSuitCardConfig(userInfo,?consumeConfigInfo);
??//?判斷當(dāng)前卡用戶是否允許消費(fèi)
??if?(consumeCardConfig?!=?null)?{
???if?((!CAN_PAY.equals(cardConsumeConfig?.getEnabledPay())))?{
???????return?false;
???}
??}
????????????????//其余情況,繼續(xù)往后傳遞
????????????return?filterChain.doFilter(filterChain,?memberInfo,?consumeConfig);
????????}
????//DayConsumeTimesConsumeLimitFilter.doFilter方法
?????public?boolean?doFilter(ConsumeLimitFilterChain?filterChain,UserInfo?userInfo,
????ConsumeConfigInfo?consumeConfigInfo?){
????????????????//獲取某一類型的消費(fèi)限制,比如student可以消費(fèi)2次
??CardConsumeConfig?cardConsumeConfig?=?findSuitCardConfig(userInfo,?consumeConfigInfo);
????????????????//獲取當(dāng)前用戶今天的消費(fèi)次數(shù)
????????????????int?consumeCnt?=?getConsumeCnt(userInfo)??
??if(consumeCnt?>=?cardConsumeConfig.getDayConsumeTimesLimit()){
????????????????????return?false;
????????????????}
????????????????//其余情況,繼續(xù)往后傳遞
????????????????return?filterChain.doFilter(filterChain,?memberInfo,?consumeConfig);
????????}
總結(jié)
策略設(shè)計(jì)模式
定義
定義一系列的算法,把每一個(gè)算法封裝起來(lái), 并且使它們可相互替換
適用場(chǎng)景
實(shí)踐經(jīng)驗(yàn)
/**
?*?推送策略
?*?/
public?interface?PushStrategy?{
?/**
?????????@param?deviceVO設(shè)備對(duì)象,包扣設(shè)備sn,信鴿pushid
?????????@param?content,推送內(nèi)容,一般為json
????????*/
?public?CallResult?push(AppDeviceVO?deviceVO,?Object?content);
}
IotPushStrategy?implements?PushStrategy{
????????/**
?????????@param?deviceVO設(shè)備對(duì)象,包扣設(shè)備sn,信鴿pushid
?????????@param?content,推送內(nèi)容,一般為json
????????*/
?public?CallResult?push(AppDeviceVO?deviceVO,?Object?content){
????????????//創(chuàng)建自研推送平臺(tái)需要的推送報(bào)文
????????????Message?message?=?createPushMsg(deviceVO,content);
????????????//調(diào)用推送平臺(tái)推送接口
????????????IotMessageService.pushMsg(message);
????????}
}
XingePushStrategy?implements?PushStrategy{
????????/**
?????????@param?deviceVO設(shè)備對(duì)象,包扣設(shè)備sn,信鴿pushid
?????????@param?content,推送內(nèi)容,一般為json
????????*/
?public?CallResult?push(AppDeviceVO?deviceVO,?Object?content){
????????????//創(chuàng)建信鴿平臺(tái)需要的推送報(bào)文
????????????JSONObject?jsonObject?=?createPushMsg(content);
????????????//調(diào)用推送平臺(tái)推送接口
????????????if(!XinggePush.pushMsg(message)){
????????????????//降級(jí)到長(zhǎng)輪詢
????????????????...
????????????}
????????}
}
/**
消息推送Service
*/
MessagePushService{
????pushMsg(AppDeviceVO?deviceVO,?Object?content){
????????if(A設(shè)備){
????????????XingePushStrategy.push(deviceVO,content);
????????}?else?if(B設(shè)備){
????????????IotPushStrategy.push(deviceVO,content);
????????}
????}
}
總結(jié)
模板設(shè)計(jì)模式
定義
使用場(chǎng)景
不同場(chǎng)景的處理流程,部分邏輯是通用的,可以放到父類中作為通用實(shí)現(xiàn),部分邏輯是個(gè)性化的,需要子類去個(gè)性實(shí)現(xiàn)。
實(shí)踐經(jīng)驗(yàn)
還是接著之前語(yǔ)音播報(bào)的例子來(lái)說(shuō),后期我們新加了兩個(gè)需求:
1.消息推送需要增加trace?
2.有些通道推送失敗需要重試 所以現(xiàn)在的流程變成了這樣:
1.trace開(kāi)始?
2.通道開(kāi)始推送?
3.是否允許重試,如果允許執(zhí)行重試邏輯?
4.trace結(jié)束 其中1和4是通用的,2和3是個(gè)性化的,鑒于此我在具體的推送策略之前增加了一層父類的策略,將通用邏輯放到了父類中,修改后的代碼如下:
abstract?class?AbstractPushStrategy?implements?PushStrategy{
????@Override
????public?CallResult?push(AppDeviceVO?deviceVO,?Object?content)?{
????????//1.構(gòu)造span
????????Span?span =?buildSpan();
????????//2.具體通道推送邏輯由子類實(shí)現(xiàn)
????????CallResult?callResult?=?doPush(deviceVO,?content);
????????//3.是否允許重試邏輯由子類實(shí)現(xiàn),如果允許執(zhí)行重試邏輯
????????if(!callResult.isSuccess()?&&?canRetry()){
????????????doPush(deviceVO,?content);
????????}
????????//4.trace結(jié)束
????????span.finish()?
????}
????//具體推送邏輯由子類實(shí)現(xiàn)
????protected?abstract?CallResult?doPush(AppDeviceVO?deviceDO,?Object?content)?;
????//是否允許重試由子類實(shí)現(xiàn),有些通道之前沒(méi)有做消息排重,所有不能重試
????protected?abstract?boolean?canRetry(CallResult?callResult);
}
XingePushStrategy?extends?AbstractPushStrategy{
????@Override
????protected?CallResult?doPush(AppDeviceVO?deviceDO,?Object?content)?{
????????//執(zhí)行推送邏輯
????}
????@Override
????protected?boolean?canRetry(CallResult?callResult){
????????return?false
????}
}
總結(jié)
通過(guò)模板定義了流程,將通用邏輯放在父類實(shí)現(xiàn),減少了重復(fù)代碼,個(gè)性化邏輯由子類自己實(shí)現(xiàn),子類間修改代碼互不干擾也不會(huì)破壞流程。
觀察者設(shè)計(jì)模式
模式定義
使用場(chǎng)景
實(shí)踐經(jīng)驗(yàn)
代碼結(jié)構(gòu)大體如下:
/**
支付回調(diào)處理者
*/
PayCallBackController?implements?ApplicationContextAware?{
?????private?ApplicationContext?applicationContext;
????//如果想獲取applicationContext需要實(shí)現(xiàn)ApplicationContextAware接口,Spring容器會(huì)回調(diào)setApplicationContext方法將applicationContext注入進(jìn)來(lái)
????@Override
????public?void?setApplicationContext(ApplicationContext?applicationContext)
????????????throws?BeansException?{
????????this.applicationContext?=?applicationContext;
????}
?????@RequestMapping(value?=?"/pay/callback.do")
?????public?View?callback(HttpServletRequest?request){
????????if(paySuccess(request){
????????????//構(gòu)造支付成功事件
????????????PaySuccessEvent?event?=?buildPaySuccessEvent(...);
????????????//通過(guò)applicationContext發(fā)布事件,從而達(dá)到通知觀察者的目的
????????????this.applicationContext.publishEvent(event);
????????}?
????}
}
/**
?*?語(yǔ)音播報(bào)處理者
?*
?*/
public?class?VoiceBroadcastHandler?implements?ApplicationListener<PaySuccessEvent>{
????@Override
????public?void?onApplicationEvent(PaySuccessEvent?event)?{
????????//語(yǔ)音播報(bào)邏輯
????}
}
//其他處理者的邏輯類似
總結(jié)
觀察者模式將被觀察者和觀察者之間做了解耦,觀察者存在與否不會(huì)影響被觀察者的現(xiàn)有邏輯。
裝飾器設(shè)計(jì)模式
定義
使用場(chǎng)景
希望對(duì)原有類的功能做增強(qiáng),但又不希望增加過(guò)多子類時(shí),可以使用裝飾器模式來(lái)達(dá)到同樣的效果。
實(shí)踐經(jīng)驗(yàn)
/**
可以自動(dòng)攜帶trace上下文的Runnable裝飾器
*/
public?class?TraceRunnableWrapper?implements?Runnable{
????//被包裝的目標(biāo)對(duì)象
????private?Runnable?task;
????private?Span?parentSpan?=?null;
????public?TraceRunnableWrapper(Runnable?task)?{
????????//1.獲取當(dāng)前線程的上下文(因?yàn)閚ew的時(shí)候還沒(méi)有發(fā)生線程切換,所以需要在這里將上下文獲取)
????????//對(duì)這塊代碼感興趣的可以查看opentracing?API
????????io.opentracing.Scope?currentScope?=?GlobalTracer.get().scopeManager().active();
????????//2.保存父上下文
????????parentSpan?=?currentScope.span();
????????this.task?=?task;
????}
????@Override
????public?void?run()?{
????????//run的時(shí)候?qū)⒏妇€程的上下文綁定到當(dāng)前線程
????????io.opentracing.Scope?scope?=?GlobalTracer.get().scopeManager().activate(parentSpan,false);
????????task.run();
????}
}
//使用者
new?Thread(new?Runnable(){run(...)}).start()替換為new?TraceRunnableWrapper(new?Runnable(){run(...)}).start()
總結(jié)
使用裝飾器模式做了功能的增強(qiáng),對(duì)使用者來(lái)說(shuō)只需要做簡(jiǎn)單的組合就能繼續(xù)使用原功能。
外觀設(shè)計(jì)模式
定義
使用場(chǎng)景
降低使用者的復(fù)雜度,簡(jiǎn)化客戶端的接入成本。
實(shí)踐經(jīng)驗(yàn)
大概代碼如下:
使用者:
HttpClient.doPost("/gateway.do","{'method':'trade.create','sign':'wxxaaa','timestamp':'15311111111'},'bizContent':'業(yè)務(wù)參數(shù)'")
GatewayController:
@RequestMapping("/gateway.do")
JSON?gateway(HttpServletRequest?req){
???//1.組裝開(kāi)放請(qǐng)求
???OpenRequest?openRequest?=?buildOpenRequest(req);
???OpenResponse?openResponse?=?null;
???//2.請(qǐng)求路由
???if("trade.create".equals(openRequest.getMethod()){
???????//proxy?to?trade?service?by?dubbo
???????openResponse?=?TradeFacade.execute(genericParam);
???}?else?if("iot.message.push".equals(openRequest.getMethod()){
???????//proxy?to?iot?service?by?httpclient
????????openResponse?=?HttpClient.doPost('http://iot.service/generic/execute'genericParam);
???}
???if(openResponse.isSuccess()){
????????return?{"code":"10000","bizContent":openResponse.getResult()};
???}else{
????????return?{"code":"20000","bizCode":openResponse.getCode()};
???}
}
總結(jié)
采用外觀模式屏蔽了系統(tǒng)內(nèi)部的一些細(xì)節(jié),降低了使用者的接入成本,就拿GatewayController來(lái)說(shuō),ISV的鑒權(quán),接口的驗(yàn)簽等重復(fù)工作統(tǒng)一由它實(shí)現(xiàn),ISV對(duì)接不同的接口只需要關(guān)心一套接口協(xié)議接口,由GatewayController這一層做了收斂。
END
免費(fèi)領(lǐng)取 1000+ 道面試資料!!小編這里有一份面試寶典《Java 核心知識(shí)點(diǎn).pdf》,覆蓋了 JVM,鎖、高并發(fā)、Spring原理、微服務(wù)、數(shù)據(jù)庫(kù)、Zookeep人、數(shù)據(jù)結(jié)構(gòu)等等知識(shí)點(diǎn),包含 Java 后端知識(shí)點(diǎn) 1000+ 個(gè),部分如下:




