從開始接觸設計模式的“什麼都可以用設計模式去設計”,到接觸一段時間之後的“用什麼設計模式,直接莽就是了”。一直到現在,對設計模式的認知一直都處在一個模糊的狀態。之前雖然也自己嘗試在專案中使用一些設計模式,但總是體會不到設計模式在實際使用中的好處,直到最近,接觸到了一種業務場景,非常適合使用工廠模式去實現的業務場景,著實耐不住心中的激動,一定要記錄下來。。。
之前對工廠設計模式的認知一直是:這不就是簡單的i根據不同的f--else的分支,建立不同的實現類嘛,如果需要擴充套件,根據不同的情況再去擴充套件不同的實現類而已嘛,這有什麼難的嘛...,想來,這樣的理解倒也不能說錯,只是感覺非常的假大空。
接下來 我們從業務角度出發,設身處地的去感受使用設計模式和不使用設計模式的區別。
假設:我們的業務場景如下
/** * 既定業務場景: * 前情提要: * 我們需要做一個檔案上傳解析的功能,假設現在檔案已經上傳上來,我們需要對資料進行解析,將資料拆解 * * 需求: * 假設是一個目錄結構,按照章節的不同,我們總共分為700章,每一章的目錄結構層級都不相同。 * 例200章如下所示: * 名稱:200章-架構-思想-設計模式-工廠模式 * code:200-JG-SX-SJMS-GCMS * 例100章如下所示: * 名稱:100章-技術-C#-委託 * code:100-JS-C#-Delegate * * 假設使用者上傳的時候,只會傳進來Code,我們需要根據Code獲取到章節、類別。 * 現知道規則如下: * 1.所有的章節code,第一個字元為章節 * 2.200章倒數第二,第三位為類別,100章倒數第三位為類別。 */
我們現在只有一百章和二百章的資料結構和處理規則,但是領導說,我們這個東西,肯定是要把所有的章節都進行處理的。我們先來看一下不使用設計模式的處理方式
1 List<string> codeList = new List<string>(); 2 codeList.Add("200-JG-SX-SJMS-GCMS"); 3 codeList.Add("100-JS-C#-Delegate"); 4 foreach (var code in codeList) 5 { 6 string Chapter = code.Split('-')[0]; 7 if (Chapter.Equals("100")) 8 { 9 string chapter100 = code.Split('-')[0]; 10 string Type100 = code.Split('-')[1]; 11 Console.WriteLine($"code:{code},章節:{chapter100},型別:{Type100}"); 12 } 13 else if (Chapter.Equals("200")) 14 { 15 string chapter400 = code.Split('-')[0]; 16 string Type400 = $"{code.Split('-')[2]}-{code.Split('-')[3]}"; 17 Console.WriteLine($"code:{code},章節:{chapter400},型別:{Type400}"); 18 } 19 }
處理思路是先迴圈集合,然後拆出來章節。根據章節去選擇不同的分支。假設我們再有不同的章節,再新增elseIf就可以了。這個例子處理比較簡單,程式碼看起來也不是很亂,但是如果我們的每一個章節的處理邏輯都比較複雜的時候,程式碼會亂成一團,首先不美觀,其次。我們如果要新增不同的分支,在原有程式碼上再加分支,分支變多以後,程式碼會變得很長,別人會難以理解。好處是程式碼結構清晰,便於理解
如果我使用工廠模式去建立:
public interface Idisassemble { /** * 拆解的介面,包含拆解的基本方法,包含獲取章節和類別 */ string GetChapter(string code); string GetType(string code); } /// <summary> /// 100章擷取工廠 /// </summary> public class DisassembleOneChapter : Idisassemble { public string GetChapter(string code) { string Chapter= code.Split('-')[0]; return Chapter; } public string GetType(string code) { string Chapter = code.Split('-')[1]; return Chapter; } } /// <summary> /// 400章的擷取工廠 /// </summary> public class DisassembleTwoChapter : Idisassemble { public string GetChapter(string code) { string Chapter = code.Split('-')[0]; return Chapter; } public string GetType(string code) { string [] codeList= code.Split('-'); string Chapter = $"{codeList[2]}-{codeList[3]}"; return Chapter; } } public static class BuildFactory { public static Idisassemble Build(string code) { Idisassemble idisassemble = null; string Chapter = code.Split('-')[0]; switch (Chapter) { case "100": idisassemble = new DisassembleOneChapter(); break; case "200": idisassemble = new DisassembleTwoChapter(); break; default: break; } return idisassemble; } }
使用方式:
static void Main(string[] args) { List<string> codeList = new List<string>(); codeList.Add("200-JG-SX-SJMS-GCMS"); codeList.Add("100-JS-C#-Delegate"); foreach (var code in codeList) { Idisassemble idisassemble = BuildFactory.Build(code); Console.WriteLine($"code:{code},章節:{idisassemble.GetChapter(code)},型別:{idisassemble.GetType(code)}"); } Console.ReadLine(); }
首先工廠模式的好處是:我可以用一個通用的介面,根據業務的不同,選擇不同的實現方式,這樣我們不會將所有的實現都在一個方法中實現,不會出現很長的程式碼段。
我們基於這樣的一個思路去實現,首先我們需要設計一個介面,這個介面囊括了所有的處理抽象
public interface Idisassemble { /** * 拆解的介面,包含拆解的基本方法,包含獲取章節和類別 */ string GetChapter(string code); string GetType(string code); }
有了這個介面以後,我們需要根據他不同的業務型別建立不同的工廠例項,分析我們的業務中目前有一百章和兩百章兩種不同的實現分支。所以我們對應的有一百章和兩百章兩個不同的實現工廠。
/// <summary> /// 100章擷取工廠 /// </summary> public class DisassembleOneChapter : Idisassemble { public string GetChapter(string code) { string Chapter= code.Split('-')[0]; return Chapter; } public string GetType(string code) { string Chapter = code.Split('-')[1]; return Chapter; } } /// <summary> /// 400章的擷取工廠 /// </summary> public class DisassembleTwoChapter : Idisassemble { public string GetChapter(string code) { string Chapter = code.Split('-')[0]; return Chapter; } public string GetType(string code) { string [] codeList= code.Split('-'); string Chapter = $"{codeList[2]}-{codeList[3]}"; return Chapter; } }
我們這兩個實現工廠繼承介面,並且實現自己的邏輯。到目前為止,我們的抽象和工廠實現都有了,有了這個工廠以後,我們就可以根據不同的業務用多型的方式去呼叫不同的工廠了。我們考慮一個問題,呼叫不同的工廠可以由使用者去自行判斷,是不是也可以由我們的程式自己去判斷出來?,由我們的程式自己判斷出來的話,我們是不是可以使用策略模式的思想去實現?
到這裡,我們引入一個新的概念,策略模式,策略模式是在程式執行時去判斷我到底需要走哪個分支。
基於這個思想,我們是不是可以有一個建立具體工廠的策略管理類?
public static class BuildFactory { public static Idisassemble Build(string code) { Idisassemble idisassemble = null; string Chapter = code.Split('-')[0]; switch (Chapter) { case "100": idisassemble = new DisassembleOneChapter(); break; case "200": idisassemble = new DisassembleTwoChapter(); break; default: break; } return idisassemble; } }
這個類我們需要知道使用者輸入什麼,根據使用者的不同輸入,解析出來不同的分支,建立不同的實現工廠。但是我們在實際業務用的時候,最終使用的是工廠類中的某一個方法。所以,我們這個類只能是返回介面,而不是具體的實現工廠。我們在當前這個假定的業務中,可以根據code拆分出來具體使用哪個工廠。實際業務中可能會更復雜。
有了這個策略建立類。我們的具體使用就比較簡單了。我們只需要呼叫策略構建類,傳入code就可以了
static void Main(string[] args) { List<string> codeList = new List<string>(); codeList.Add("200-JG-SX-SJMS-GCMS"); codeList.Add("100-JS-C#-Delegate"); foreach (var code in codeList) { Idisassemble idisassemble = BuildFactory.Build(code); Console.WriteLine($"code:{code},章節:{idisassemble.GetChapter(code)},型別:{idisassemble.GetType(code)}"); } Console.ReadLine(); }
到目前,我們的工廠建立已經完成了,但是,我們觀察這兩個工廠,它裡面的獲取章節的方法是重複的。業務簡單抄一份倒也無所謂,但是如果業務複雜,抄一份的工作量還是很大的,而且從設計的角度來看,我們所有重複的,都可以抽出來。
如果我們繼承介面的不再直接是具體的工廠,而是一個可重寫的虛實體,將通用的方法寫在這個虛實體裡邊,不一樣的方法再具體的工廠裡邊具體實現。
public interface Idisassemble { /** * 拆解的介面,包含拆解的基本方法,包含獲取章節和類別 */ string GetChapter(string code); string GetType(string code); } public abstract class DisassembleAbtract : Idisassemble { public string GetChapter(string code) { string Chapter = code.Split('-')[0]; return Chapter; } public abstract string GetType(string code); } /// <summary> /// 100章擷取工廠 /// </summary> public class DisassembleOneChapter : DisassembleAbtract { //public string GetChapter(string code) //{ // string Chapter= code.Split('-')[0]; // return Chapter; //} public override string GetType(string code) { string Chapter = code.Split('-')[1]; return Chapter; } } /// <summary> /// 400章的擷取工廠 /// </summary> public class DisassembleTwoChapter : DisassembleAbtract { //public string GetChapter(string code) //{ // string Chapter = code.Split('-')[0]; // return Chapter; //} public override string GetType(string code) { string [] codeList= code.Split('-'); string Chapter = $"{codeList[2]}-{codeList[3]}"; return Chapter; } }
封裝,繼承,多型,虛方法。。。?