對工廠模式一次感悟

碼不夠的張小墨發表於2023-05-12

從開始接觸設計模式的“什麼都可以用設計模式去設計”,到接觸一段時間之後的“用什麼設計模式,直接莽就是了”。一直到現在,對設計模式的認知一直都處在一個模糊的狀態。之前雖然也自己嘗試在專案中使用一些設計模式,但總是體會不到設計模式在實際使用中的好處,直到最近,接觸到了一種業務場景,非常適合使用工廠模式去實現的業務場景,著實耐不住心中的激動,一定要記錄下來。。。

之前對工廠設計模式的認知一直是:這不就是簡單的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;
        }
    }

封裝,繼承,多型,虛方法。。。?

相關文章