實戰(zhàn)!工作中常用到哪些設(shè)計模式
時間:2021-11-08 15:45:47
手機看文章
掃描二維碼
隨時隨地手機看文章
[導讀]前言平時我們寫代碼呢,多數(shù)情況都是流水線式寫代碼,基本就可以實現(xiàn)業(yè)務(wù)邏輯了。如何在寫代碼中找到樂趣呢,我覺得,最好的方式就是:使用設(shè)計模式優(yōu)化自己的業(yè)務(wù)代碼。今天跟大家聊聊日常工作中,我都使用過哪些設(shè)計模式。工作中常用到哪些設(shè)計模式1.策略模式1.1業(yè)務(wù)場景假設(shè)有這樣的業(yè)務(wù)場景,...
前言
平時我們寫代碼呢,多數(shù)情況都是流水線式寫代碼,基本就可以實現(xiàn)業(yè)務(wù)邏輯了。如何在寫代碼中找到樂趣呢,我覺得,最好的方式就是:使用設(shè)計模式優(yōu)化自己的業(yè)務(wù)代碼。今天跟大家聊聊日常工作中,我都使用過哪些設(shè)計模式。
1.策略模式
1.1 業(yè)務(wù)場景
假設(shè)有這樣的業(yè)務(wù)場景,大數(shù)據(jù)系統(tǒng)把文件推送過來,根據(jù)不同類型采取不同的解析方式。多數(shù)的小伙伴就會寫出以下的代碼:
//按照A格式解析
}else if(type=="B"){
//按B格式解析
}else{
//按照默認格式解析
}
這個代碼可能會存在哪些問題呢?
- 如果分支變多,這里的代碼就會變得臃腫,難以維護,可讀性低。
- 如果你需要接入一種新的解析類型,那只能在原有代碼上修改。
- 開閉原則(對于擴展是開放的,但是對于修改是封閉的):增加或者刪除某個邏輯,都需要修改到原來代碼
- 單一原則(規(guī)定一個類應(yīng)該只有一個發(fā)生變化的原因):修改任何類型的分支邏輯代碼,都需要改動當前類的代碼。
if...else等條件分支,并且每個條件分支,可以封裝起來替換的,我們就可以使用策略模式來優(yōu)化。
1.2 策略模式定義
策略模式定義了算法族,分別封裝起來,讓它們之間可以相互替換,此模式讓算法的變化獨立于使用算法的的客戶。這個策略模式的定義是不是有點抽象呢?那我們來看點通俗易懂的比喻:
假設(shè)你跟不同性格類型的小姐姐約會,要用不同的策略,有的請電影比較好,有的則去吃小吃效果不錯,有的去逛街買買買最合適。當然,目的都是為了得到小姐姐的芳心,請看電影、吃小吃、逛街就是不同的策略。
策略模式針對一組算法,將每一個算法封裝到具有共同接口的獨立的類中,從而使得它們可以相互替換。
1.3 策略模式使用
策略模式怎么使用呢?醬紫實現(xiàn)的:
-
一個接口或者抽象類,里面兩個方法(一個方法匹配類型,一個可替換的邏輯實現(xiàn)方法)
-
不同策略的差異化實現(xiàn)(就是說,不同策略的實現(xiàn)類)
-
使用策略模式
1.3.1 一個接口,兩個方法
public interface IFileStrategy {
//屬于哪種文件解析類型
FileTypeResolveEnum gainFileType();
//封裝的公用算法(具體的解析方法)
void resolve(Object objectparam);
}
1.3.2 不同策略的差異化實現(xiàn)
A 類型策略具體實現(xiàn)
@Component
public class AFileResolve implements IFileStrategy {
@Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.File_A_RESOLVE;
}
@Override
public void resolve(Object objectparam) {
logger.info("A 類型解析文件,參數(shù):{}",objectparam);
//A類型解析具體邏輯
}
}
B 類型策略具體實現(xiàn)
@Component
public class BFileResolve implements IFileStrategy {
@Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.File_B_RESOLVE;
}
@Override
public void resolve(Object objectparam) {
logger.info("B 類型解析文件,參數(shù):{}",objectparam);
//B類型解析具體邏輯
}
}
默認類型策略具體實現(xiàn)
@Component
public class DefaultFileResolve implements IFileStrategy {
@Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.File_DEFAULT_RESOLVE;
}
@Override
public void resolve(Object objectparam) {
logger.info("默認類型解析文件,參數(shù):{}",objectparam);
//默認類型解析具體邏輯
}
}
1.3.3 使用策略模式
如何使用呢?我們借助spring的生命周期,使用ApplicationContextAware接口,把對用的策略,初始化到map里面。然后對外提供resolveFile方法即可。
/**
* @author 公眾號:撿田螺的小男孩
*/
@Component
public class StrategyUseService implements ApplicationContextAware{
private Map iFileStrategyMap = new ConcurrentHashMap<>();
public void resolveFile(FileTypeResolveEnum fileTypeResolveEnum, Object objectParam) {
IFileStrategy iFileStrategy = iFileStrategyMap.get(fileTypeResolveEnum);
if (iFileStrategy != null) {
iFileStrategy.resolve(objectParam);
}
}
//把不同策略放到map
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map tmepMap = applicationContext.getBeansOfType(IFileStrategy.class);
tmepMap.values().forEach(strategyService -> iFileStrategyMap.put(strategyService.gainFileType(), strategyService));
}
}
2. 責任鏈模式
2.1 業(yè)務(wù)場景
我們來看一個常見的業(yè)務(wù)場景,下訂單。下訂單接口,基本的邏輯,一般有參數(shù)非空校驗、安全校驗、黑名單校驗、規(guī)則攔截等等。很多伙伴會使用異常來實現(xiàn):
public class Order {
public void checkNullParam(Object param){
//參數(shù)非空校驗
throw new RuntimeException();
}
public void checkSecurity(){
//安全校驗
throw new RuntimeException();
}
public void checkBackList(){
//黑名單校驗
throw new RuntimeException();
}
public void checkRule(){
//規(guī)則攔截
throw new RuntimeException();
}
public static void main(String[] args) {
Order order= new Order();
try{
order.checkNullParam();
order.checkSecurity ();
order.checkBackList();
order2.checkRule();
System.out.println("order success");
}catch (RuntimeException e){
System.out.println("order fail");
}
}
}
這段代碼使用了異常來做邏輯條件判斷,如果后續(xù)邏輯越來越復雜的話,會出現(xiàn)一些問題:如異常只能返回異常信息,不能返回更多的字段,這時候需要自定義異常類。
并且,阿里開發(fā)手冊規(guī)定:禁止用異常做邏輯判斷。
【強制】 異常不要用來做流程控制,條件控制。說明:異常設(shè)計的初衷是解決程序運行中的各種意外情況,且異常的處理效率比條件判斷方式要低很多。
如何優(yōu)化這段代碼呢?可以考慮責任鏈模式
2.2 責任鏈模式定義
當你想要讓一個以上的對象有機會能夠處理某個請求的時候,就使用責任鏈模式。
責任鏈模式為請求創(chuàng)建了一個接收者對象的鏈。執(zhí)行鏈上有多個對象節(jié)點,每個對象節(jié)點都有機會(條件匹配)處理請求事務(wù),如果某個對象節(jié)點處理完了,就可以根據(jù)實際業(yè)務(wù)需求傳遞給下一個節(jié)點繼續(xù)處理或者返回處理完畢。這種模式給予請求的類型,對請求的發(fā)送者和接收者進行解耦。
責任鏈模式實際上是一種處理請求的模式,它讓多個處理器(對象節(jié)點)都有機會處理該請求,直到其中某個處理成功為止。責任鏈模式把多個處理器串成鏈,然后讓請求在鏈上傳遞:
責任鏈模式打個比喻:
假設(shè)你晚上去上選修課,為了可以走點走,坐到了最后一排。來到教室,發(fā)現(xiàn)前面坐了好幾個漂亮的小姐姐,于是你找張紙條,寫上:“你好, 可以做我的女朋友嗎?如果不愿意請向前傳”。紙條就一個接一個的傳上去了,后來傳到第一排的那個妹子手上,她把紙條交給老師,聽說老師40多歲未婚...
2.3 責任鏈模式使用
責任鏈模式怎么使用呢?
-
一個接口或者抽象類
-
每個對象差異化處理
-
對象鏈(數(shù)組)初始化(連起來)
2.3.1 一個接口或者抽象類
這個接口或者抽象類,需要:
-
有一個指向責任下一個對象的屬性
-
一個設(shè)置下一個對象的set方法
-
給子類對象差異化實現(xiàn)的方法(如以下代碼的doFilter方法)
/**
* 關(guān)注公眾號:撿田螺的小男孩
*/
public abstract class AbstractHandler {
//責任鏈中的下一個對象
private AbstractHandler nextHandler;
/**
* 責任鏈的下一個對象
*/
public void setNextHandler(AbstractHandler nextHandler){
this.nextHandler = nextHandler;
}
/**
* 具體參數(shù)攔截邏輯,給子類去實現(xiàn)
*/
public void filter(Request request, Response response) {
doFilter(request, response);
if (getNextHandler() != null) {
getNextHandler().filter(request, response);
}
}
public AbstractHandler getNextHandler() {
return nextHandler;
}
abstract void doFilter(Request filterRequest, Response response);
}
2.3.2 每個對象差異化處理
責任鏈上,每個對象的差異化處理,如本小節(jié)的業(yè)務(wù)場景,就有參數(shù)校驗對象、安全校驗對象、黑名單校驗對象、規(guī)則攔截對象
/**
* 參數(shù)校驗對象
**/
@Component
@Order(1) //順序排第1,最先校驗
public class CheckParamFilterObject extends AbstractHandler {
@Override
public void doFilter(Request request, Response response) {
System.out.println("非空參數(shù)檢查");
}
}
/**
* 安全校驗對象
*/
@Component
@Order(2) //校驗順序排第2
public class CheckSecurityFilterObject extends AbstractHandler {
@Override
public void doFilter(Request request, Response response) {
//invoke Security check
System.out.println("安全調(diào)用校驗");
}
}
/**
* 黑名單校驗對象
*/
@Component
@Order(3) //校驗順序排第3
public class CheckBlackFilterObject extends AbstractHandler {
@Override
public void doFilter(Request request, Response response) {
//invoke black list check
System.out.println("校驗黑名單");
}
}
/**
* 規(guī)則攔截對象
*/
@Component
@Order(4) //校驗順序排第4
public class CheckRuleFilterObject extends AbstractHandler {
@Override
public void doFilter(Request request, Response response) {
//check rule
System.out.println("check rule");
}
}