消除if-else/switch语句块来聚合模型的设计与实现
生活随笔
收集整理的這篇文章主要介紹了
消除if-else/switch语句块来聚合模型的设计与实现
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
寫在最前頭的話:請不要理解為不再需要if-else/switch。寫在最前頭的結論:使用Enum。 1, 前言 if/switch這樣的分支語句在實際開發(fā)中的使用自然是不可避免,但是我們必須承認使用這種分支判斷語句實現(xiàn)的代碼不僅可讀性差(轉來轉去的繞暈),而且維護代價極高。導致維護代價上升,個人認為地并不是說是由于在開發(fā)軟件時,開發(fā)人員基礎不夠好或者問題考慮不周全導致的各種漏洞和缺陷,主要原因是沒有很好的遵循我們聽爛了的軟件開發(fā)基本原則-高內聚低耦合。在業(yè)務系統(tǒng)的開發(fā)過程中,大多業(yè)務需求都不可能是完全在一條分支完成的,而如果系統(tǒng)核心業(yè)務是要完成多個渠道、機構或者系統(tǒng)的對接,這時候應該怎么設計業(yè)務系統(tǒng)使得業(yè)務邏輯高度聚合呢?初始拿到這個需求的時候,腦子里可能會這樣一個換面:你手持if/switch牧羊棍,驅使著千萬頭草尼瑪。既然你的核心業(yè)務是要對接多個系統(tǒng)、而這些系統(tǒng)又是異構的,沒有統(tǒng)一規(guī)范,if/switch是可以解決你的問題,但是它們很可能把你引入一個混亂的系統(tǒng),因為它們已經(jīng)把你的業(yè)務邏輯散落各處,到后面你的任何一個修改都可能是牽一發(fā)而動全身。 文字表述費勁、也不能具體說明問題,下面還是show my code吧。 2,問題描述 在這兒,設定一個業(yè)務場景。假定要開發(fā)一套對接渠道和交易所(或銀行或登記結算中心,無論是渠道還是交易所都可以統(tǒng)一抽象為機構)的清算系統(tǒng),對于清算系統(tǒng)的業(yè)務功能,具體可以拿我們大家都熟悉的支付寶這樣的第三方支付為例來說明。渠道指的就是使用支付寶作為支付的商家,商家每天或者固定一個周期,會生成在其名下的交易流水信息文件(文件在不同場景的清算系統(tǒng)里文件種類多樣),并將這些流水信息文件發(fā)給支付寶,然后支付寶接收并處理渠道端發(fā)送過來的流水信息文件,同時支付寶也要和背后的真正的資金管理方(銀行)進行相關的文件交互(這里通常是和多家銀行,每家銀行有各自不同的文件交互規(guī)范)。因此,在這個清算系統(tǒng)里,一個問題是解決各類渠道和各個銀行之間的文件交互問題(作為第三方系統(tǒng),通常你無法讓所有接入方都遵照你的一套規(guī)范來做)。拋開一些文件交互的實現(xiàn)細節(jié)問題,我們從業(yè)務角度分析其中的基本問題:文件類型多樣、文件命名各異、文件存放路徑各異。然后假定和交易所交互的文件模式形如transaction_000_product_(\d{8})_(\d{3}).req.txt(多類文件中的一類),和渠道交互的文件模式形如OFI_(\d{4})_8888_(\d{8}).TXT(多類文件中的一類)。 于是很可能會有如下代碼場景: 1 /**
2 * 根據(jù)機構給定文件名模式,按照批次日期生成對應機構的交互文件名
3 * @param institution
4 * @param fileNamePattern
5 * @param batchDate
6 */
7 public String fetchFileName(String institution, String fileNamePattern, Date batchDate) {
8 String fileName = null;
9 if(institution.equal("TA")) {
10 //交易所文件,8位日期,3位批次號
11 String fileKeyPatternWithDate = String
12 .format(fileKeyPattern.replace("(\\d{8})", "%1$tY%1$tm%1$td"), new Date());
13 int seq = sequenceDAO.fetchSequence(fileKeyPatternWithDate);
14 fileName = fileKeyPatternWithDate.replace("(\\d{3})", String.format("%03d", seq));
15 fileName = fileName.substring(fileName.lastIndexOf(File.separator)+1);
16 } else if(institution.equal("Com")){
17 //渠道文件,8位日期,4位渠道號
18 fileName = String.format(fileKeyPattern.replace("(\\d{8})", "%1$tY%1$tm%1$td"), new Date());
19 fileName.replace("(\\d{4})", InstitutionCode.Com.code());
20 }
21 return fileName;
22 }
23
24 /**
25 * 可能有各種原因,公司沒有為所有應用部署集中的ftp服務器或者純粹只是建立一個臨時方案,導致你需要針對不同的機構配置不同的ftp交互策略。
26 * 于是你可能有如下兩個獲取ftp download和upload目錄的方法。
27 */
28 public String getDownloadFileDir(String institution, Date batchDate){
29 String date = new SimpleDateFormat("yyyyMMdd").format(batchDate);
30 //sftpBaseDir是使用map存放的關于各個機構交互文件的sftp父級目錄
31 if(institution.equal("TA")){
32 return sftpBaseDir.get(institution) + "/" + date;
33 } else if(institution.equal("Com")){
34 return sftpBaseDir.get(institution) + "/download/" + date;
35 }
36
37 return null;
38 }
39 public String getUploadFileDir(String institution, Date batchDate){
40 String date = new SimpleDateFormat("yyyyMMdd").format(batchDate);
41 //sftpBaseDir是使用map存放的關于各個機構交互文件的sftp父級目錄
42 if(institution.equal("TA")){
43 return sftpBaseDir.get(institution) + "/" + date;
44 } else if(institution.equal("Com")){
45 return sftpBaseDir.get(institution) + "/upload/" + date;
46 }
47 return null;
48 }
49
50 /**
51 * 由于文件模式固定,你的業(yè)務需求里很可能期望能通過對應機構的文件名就能推斷該文件的一些詳細信息.
52 */
53 public InstitutionFile inferFileInfoByFilename(String institution, String filename){
54 String filepattern = null;
55 if(institution.equal("TA")){
56 String patterns[] = fileName.split("\\d{8}",2);
57 String tmpPattern = patterns[0] + "(\\d{8})" + patterns[1];
58 patterns = tmpPattern.split("\\d{3}[.]", 2);
59 filepattern = patterns[0] + "(\\d{3})." + patterns[1];
60 } else if(institutuion.equal("Com")){
61 String patterns[] = fileName.split("\\d{8}", 2);
62 String tmpPattern = patterns[0] + "(\\d{8})" + patterns[1];
63 patterns = tmpPattern.split("\\d{4}", 2);
64 filepattern = patterns[0] + "(\\d{4})" + patterns[1];
65 }
66 InstitutionFile institutionFile = institutionRepository.
67 findFileByIdentity(institution.filePattern(file.getName()), institution);
68 return institutionFile;
69 } 代碼段 1 如從上面列舉的零散代碼片段來看,可讀性非常差。到處都是令人厭煩的分支判斷(接入機構增多之后,情況會更糟),散落各處的業(yè)務處理規(guī)則和硬編碼,完全沒有拓展性可言。因此,我們有必要去認真地抽象和設計模型,使得業(yè)務邏輯更加聚合,代碼更易維護和拓展。 3,抽象模型
?
圖1 機構交互文件抽象模型 圖1展示的領域模型的核心是Institution,對應的實現(xiàn)是enum類。Institution中針對每一個機構都定義了一份系統(tǒng)唯一的單例對象,所以每一個Institution對象也有在IFile中定義的,自己獨立的文件交互業(yè)務的空間。InstitutionFile是作為系統(tǒng)內所有相關的機構文件的頂層抽象。 4,代碼重構 下面看一下Institution核心實現(xiàn),代碼實際也很簡單,基本都是提取自代碼片段1中的代碼。 Institution.java 1 public enum Institution implements IInstitution, IFile { 2 3 TA("交易所"){ 4 @Override 5 public Type type() { 6 return Type.TA; 7 } 8 9 @Override 10 public String fileNameWithoutSeq(String filePattern, Date batchDate) { 11 return String.format(filePattern.replace("(\\d{8})", "%1$tY%1$tm%1$td"), batchDate); 12 } 13 14 @Override 15 public String filePattern(String fileName) { 16 String patterns[] = fileName.split("\\d{8}",2); 17 String filePattern = patterns[0] + "(\\d{8})" + patterns[1]; 18 patterns = filePattern.split("\\d{3}[.]", 2); 19 return patterns[0] + "(\\d{3})." + patterns[1]; 20 } 21 22 @Override 23 public String downloadFilePath(String basePath, Date batchDate) { 24 String date = new SimpleDateFormat("yyyyMMdd").format(batchDate); 25 return basePath + "/" + date; 26 } 27 28 @Override 29 public String uploadFilePath(String basePath, Date batchDate) { 30 String date = new SimpleDateFormat("yyyyMMdd").format(batchDate); 31 return basePath + "/" + date; 32 } 33 34 @Override 35 public boolean multiBatch() { 36 return true; 37 } 38 39 @Override 40 public String fileNameWithSeq(String fileNamePatternWithDate, int seq) { 41 if(!multiBatch()) 42 throw new UnsupportedOperationException("該機構不支持一天多批次文件"); 43 return fileNamePatternWithDate.replace("(\\d{3})", String.format("%03d", seq)); 44 } 45 }, 46 Com("渠道") { 47 @Override 48 public Type type() { 49 return Type.Channel; 50 } 51 52 @Override 53 public String fileNameWithoutSeq(String filePattern, Date batchDate) { 54 String fileKeyPatternWithDate = String.format(filePattern.replace("(\\d{8})", "%1$tY%1$tm%1$td"), 55 batchDate); 56 return fileKeyPatternWithDate.replace("(\\d{4})", ChannelCode.typeOf(this).getCode()); 57 } 58 59 @Override 60 public String filePattern(String fileName) { 61 String patterns[] = fileName.split("\\d{8}", 2); 62 String filePattern = patterns[0] + "(\\d{8})" + patterns[1]; 63 patterns = filePattern.split("\\d{4}", 2); 64 return patterns[0] + "(\\d{4})" + patterns[1]; 65 } 66 67 @Override 68 public String fileNameWithSeq(String fileNamePatternWithDate, int seq) { 69 throw new UnsupportedOperationException("該機構不支持一天多批次文件"); 70 } 71 }; 72 73 private String text; 74 Institution(String text){ 75 this.text = text; 76 } 77 public String getText(){ 78 return this.text; 79 } 80 public String getAbbr(){ 81 return this.toString().toLowerCase(); 82 } 83 84 @Override 85 public Institution type() { 86 return this; 87 } 88 89 @Override 90 public InstitutionCode institutionCode() { 91 return InstitutionCode.typeOf(this); 92 } 93 94 public static Institution codeOf(String institution){ 95 for(Institution ins : Institution.values()){ 96 if(ins.getAbbr().equals(institution.toLowerCase())){ 97 return ins; 98 } 99 } 100 throw new IllegalArgumentException("不支持機構"); 101 } 102 103 @Override 104 public DateFormat fileNameDateFormat() { 105 return new SimpleDateFormat("yyyyMMdd"); 106 } 107 108 @Override 109 public boolean multiBatch() { 110 return false; 111 } 112 113 @Override 114 public String downloadFilePath(String basePath, Date batchDate) { 115 String date = new SimpleDateFormat("yyyyMMdd").format(batchDate); 116 return basePath + "/download/" + date; 117 } 118 119 @Override 120 public String uploadFilePath(String basePath, Date batchDate) { 121 String date = new SimpleDateFormat("yyyyMMdd").format(batchDate); 122 return basePath + "/upload/" + date; 123 } 124 125 } View Code 在Institution.java中,工作就是針對文件交互業(yè)務的抽象IFile,實現(xiàn)了根據(jù)不同的Institution來配置相對應的獨立或者共享的文件交互策略。 最后,按照圖1描述建立的領域模型,代碼段1中的實現(xiàn)都將重構為對應的代碼段2所示。對比代碼片段1和2,重構后的代碼變得更加簡潔,我們也得到了聚合在一起的核心領域對象Institution。 1 public String fetchFileName(Institution institution, String fileKeyPattern, Date batchDate) { 2 String fileNamePatternWithDate = institution.fileNameWithoutSeq(fileKeyPattern, batchDate); 3 if(institution.multiBatch()) { 4 int seq = sequenceDAO.fetchSequence(institution + File.separator + fileNamePatternWithDate); 5 return institution.fileNameWithSeq(fileNamePatternWithDate, seq); 6 } 7 return fileNamePatternWithDate; 8 } 9 10 public String getUploadFileDir(Institution institution, Date batchDate){ 11 return institution.uploadFilePath(sftpBaseDir.get(institution.getAbbr()), batchDate); 12 } 13 public String getDownloadFileDir(Institution institution, Date batchDate){ 14 return institution.downloadFilePath(sftpBaseDir.get(institution.getAbbr()), batchDate); 15 } 16 17 public InstitutionFile inferFileInfoByFilename(Institution institution, String filename){ 18 InstitutionFile institutionFile = institutionRepository. 19 findFileByIdentity(institution.filePattern(filename), institution); 20 return institutionFile; 21 } 代碼段 2轉載于:https://www.cnblogs.com/shenjixiaodao/p/7230123.html
總結
以上是生活随笔為你收集整理的消除if-else/switch语句块来聚合模型的设计与实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 双主动桥隔离双向DC-DC变换器(七)设
- 下一篇: Java笔记12-函数式接口