程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 一步一步造個Ioc輪子(一):Ioc是什麼,一步一步造輪子ioc

一步一步造個Ioc輪子(一):Ioc是什麼,一步一步造輪子ioc

編輯:C#入門知識

一步一步造個Ioc輪子(一):Ioc是什麼,一步一步造輪子ioc


 一步一步造個Ioc輪子目錄

 一步一步造個Ioc輪子(一):Ioc是什麼 一步一步造個Ioc輪子(二):詳解泛型工廠

 

 

前言

.net core正式版前兩天發布了,喜大普奔,借此機會,強行來寫第一篇博客

第一次寫博客,有點緊張,不知怎麼才能裝做經常寫的樣子(:

第一次,造個小輪子吧,Ioc容器,借此完善自己的類庫


DI,Ioc什麼的高大上的名字是什麼意思

我不是老司機,開C#時間不太長,以下粗淺個人見解,說錯了打臉要輕一點,畢竟是還想靠臉吃飯 (:

怕詞不達意,用個示例來說明吧,老司機繞行

小黃是一家電商公司的主程,有一天老板說我們加個發貨短信通知發短信給客戶吧

於是小黃找了一個叫XSMS的短信通道供應商簽約了

然後小黃寫了開始碼代碼了

首先寫一個XSMS的類

        public class XSMS
        {
            private string sign;
            public XSMS(string key, string pwd)
            {
                sign = "什麼都有的電商公司";
            }
            private string FormatTpl(string tpl, string msg, long phoneNumber, string client, string sign)
            {
                return tpl.Replace("$sign", sign).Replace("$msg", msg).Replace("$client", client);
            }
            public void Send(string msg, long phone, string client, string tpl)
            {
                //簽名等等把短信發到XSMS公司的接口上
                //XXX的復雜發送代碼
                Console.WriteLine("從XSMS發短信:" + FormatTpl(tpl, msg, phone, client, sign));
            }
        }

接著,開始寫業務調用了

        public static void Main(string[] args)
        {
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
            while (true)
            {
                SendMsg2Client();
                Thread.Sleep(1000 * 60);
            }
        }
        private static void SendMsg2Client()
        {
            var cs = GetClient();

            if (cs != null && cs.Count > 0)
            {
                string tpl = "[$sign]$client您好,$msg";
                var sms = new XSMS("123", "666");
                foreach (var c in cs)
                {
                    sms.Send("您訂購的充氣女友已發貨,查收後請愛惜使用", c.Key, c.Value, tpl);
                }
            }
        }

        private static void SetSend(long phone)
        {
            //寫到數據庫標記已經發了短信
        }

        private static Dictionary<long, string> GetClient()
        {
            //從數據庫裡取出需要發短信的客戶
            return new Dictionary<long, string> { { 13666666666, "群主" } };
        }

 好了,業務上線了,老板很滿意

過了好大半個月,老板找到小黃:小黃啊,我朋友那裡發個短信才4.5分一條,你簽那個什麼通道怎麼要6分一條啊,我們改為他公司的接口吧,我給他公司的技術員QQ你,你跟他聯系吧

小黃找到老板朋友公司的技術員,讓他搞了一個接口用他公司的帳號去發短信,

好了,小黃又開始加班撸代碼了

    public class FriendSMS
    {
        private string sign;
        public FriendSMS(string key, string pwd)
        {
            sign = "什麼都有的電商公司";
        }
        private string FormatTpl(string tpl, string msg, long phoneNumber, string client, string sign)
        {
            return tpl.Replace("$sign", sign).Replace("$msg", msg).Replace("$client", client);
        }
        public void Send(string msg, long phone, string client, string tpl)
        {
            //簽名等等把短信發到老板朋友公司提供的接口上
            //XXX的復雜發送代碼
            Console.WriteLine("從FriendSMS發短信:" + FormatTpl(tpl, msg, phone, client, sign));
        }
    }

撸完短信類的代碼,再去修改業務類代碼

        private static void SendMsg2Client()
        {
            var cs = GetClient();

            if (cs != null && cs.Count > 0)
            {
                string tpl = "[$sign]$client您好,$msg";
                string key = "123";//從配置文件或數據庫裡取得
                string pwd = "666";//從配置文件或數據庫裡取得
                var sms = new FriendSMS(key, pwd);//改用老板朋友那家公司的接口了
                foreach (var c in cs)
                {
                    sms.Send("您訂購的充氣女友已發貨,查收後請愛惜使用", c.Key, c.Value, tpl);
                }
            }
        }

 

好了,業務上線了,老板滿意,就是小黃累了點,給測試為難了一下:你這業務代碼都改了,又要我重新測試整個業務了,下班你要請我大保健啊

業務上線一個月,老板把小黃叫到辦公室:小黃啊,你寫那個短信怎麼老是有客戶說收不到呢,你寫什麼垃圾代碼啊,這麼不穩定

小黃辯道:老板,不關我事了,是你那朋友公司提供那接口老是不穩定,經常把我們的短信吃掉,我跟他們天天對數據,頭都大了,我跟他們技術了解過,他們是用阿裡大魚的接口,他們是寫了個代理接口給我們用,不如我們直接用阿裡大魚的接口吧,一樣是4.5分一條短信,直接對接肯定穩定很多,不會受制於你朋友的公司了

老板:好吧,弄好了請你大保健

這次小黃學乖了,媽蛋這次換阿裡大魚又要被測試罵了,怎麼樣才能讓業務代碼盡量不用改呢,百度了一番,找群裡的問了一番,小黃決定用一個簡單的實現:工廠模式

小黃開始重構代碼了,XXSMS類樣子操作方法都是一樣,我要抽象一個接口出來

    public interface ISMS
    {
        void Init(string key, string pwd);
        void Send(string msg, long phone, string client, string tpl);
    }

寫阿裡大魚及修改之前兩個通道的代碼

    public abstract class BaseSMS : ISMS
    {
        public virtual void Init(string key, string pwd)
        {
            throw new NotImplementedException();
        }

        public virtual void Send(string msg, long phone, string client, string tpl)
        {
            throw new NotImplementedException();
        }
        protected string FormatTpl(string tpl, string msg, long phoneNumber, string client, string sign)
        {
            return tpl.Replace("$sign", sign).Replace("$msg", msg).Replace("$client", client);
        }
    }
    public class AlidayuSMS : BaseSMS
    {
        private string sign;
        public override void Init(string key, string pwd)
        {
            sign = "什麼都有的電商公司";
        }
        public override void Send(string msg, long phone, string client, string tpl)
        {
            //簽名等等把短信發到XSMS公司的接口上
            //XXX的復雜發送代碼
            Console.WriteLine("從阿裡大魚發短信:" + FormatTpl(tpl, msg, phone, client, sign));
        }
    }
    public class XSMS : BaseSMS
    {
        private string sign;
        public override void Init(string key, string pwd)
        {
            sign = "什麼都有的電商公司";
        }
        public override void Send(string msg, long phone, string client, string tpl)
        {
            //簽名等等把短信發到XSMS公司的接口上
            //XXX的復雜發送代碼
            Console.WriteLine("從XSMS發短信:" + FormatTpl(tpl, msg, phone, client, sign));
        }
    }
    public class FriendSMS : BaseSMS
    {
        private string sign;
        public override void Init(string key, string pwd)
        {
            sign = "什麼都有的電商公司";
        }
        public override void Send(string msg, long phone, string client, string tpl)
        {
            //簽名等等把短信發到老板朋友公司提供的接口上
            //XXX的復雜發送代碼
            Console.WriteLine("從FriendSMS發短信:" + FormatTpl(tpl, msg, phone, client, sign));
        }
    }

 

然後寫個簡單工廠

    public class SMSFactory
    {
        public static ISMS GetSMS(string type)
        {
            switch (type.Trim().ToLower())
            {
                case "xsms": return new XSMS();
                case "friendsms": return new FriendSMS();
                case "alidayusms": return new AlidayuSMS();
            }
            throw new Exception("不存在的短信通道:" + type + "");
        }
    }

修改業務類代碼

        private static void SendMsg2Client()
        {
            var cs = GetClient();

            if (cs != null && cs.Count > 0)
            {
                string tpl = "[$sign]$client您好,$msg";
                string key = "123";//從配置文件或數據庫裡取得
                string pwd = "666";//從配置文件或數據庫裡取得
                string type = "alidayusms";//從配置文件或數據庫裡取得
                var sms = SMSFactory.GetSMS(type);
                sms.Init(key, pwd);
                foreach (var c in cs)
                {
                    sms.Send("您訂購的充氣女友已發貨,查收後請愛惜使用", c.Key, c.Value, tpl);
                }
            }
        }

上線了,很穩定而且也便宜,老板很滿意,給了50塊小黃去大保健,小黃把這個大保健的機會讓給了測試,因為測試這次怨氣大點大,業務代碼要測試,幾個原來的通道接口也要測試

然後有一天,阿裡那邊通知:開放平台接口要改為HTTPS,小黃這聽到這個消息,冷笑一聲,嘿嘿不就改一下這個類而已麼

然後小黃把阿裡大魚的類修改一下然後准備上線了,測試聽到這個消息:媽蛋你當我透明了啊,跳過我就上線啦

小黃辯稱道:就改了一下阿裡大魚那個類而已

測試:不行,你那個工廠依賴大魚,重新編譯了生成的DLL都不知跟原來業務有沒有問題

小黃暗暗叫苦,這不是變著法子騙大保健麼

讓測試再坑了一次大保健,小黃到群裡吐苦水,說測試為難自己,眾群員也跟著譴責測試,

不過其中一個老司機說:這是你自找的,怪不了測試,你應該讓業務代碼與這些短信模塊解耦,不要依賴這些模塊

小黃:老司機,請教怎麼破啊

老司機:依賴倒置,讓這業務及你這個"工廠"與各個SMS類完全無關,一個SMS類起一個項目生成一個DLL以後你改類就改那個項目,加類就加個新項目,那個模塊項目整個好交給測試就行了

小黃:那這個工廠也是要重新編譯啊,測試到時也一樣會找我麻煩

老司機:把你的工廠升級一下,做成反射工廠或者更一步到位用Ioc容器,需要什麼樣的短信模塊,交給升級的工廠或Ioc容器來完成,這樣一來,工廠或Ioc容器是完全不用變化,業務代碼更是完全不用動,以後測試也輕松了,分分鐘反過來請你去大保健

小黃及眾群員:嘀,學生卡

在這個依賴倒置的思想指導下,小黃找了一個Ioc容器,把SMS模塊寫到Ioc容器的配置文件裡

然後最後一次修改業務代碼

        private static void SendMsg2Client()
        {
            var cs = GetClient();

            if (cs != null && cs.Count > 0)
            {
                string tpl = "[$sign]$client您好,$msg";
                string key = "123";//從配置文件或數據庫裡取得
                string pwd = "666";//從配置文件或數據庫裡取得
                
                var ctx = new IocContainer("cfg.xml");
                var sms = ctx.Resolve<ISMS>();//從Ioc容器取出實現類
                sms.Init(key, pwd);
                foreach (var c in cs)
                {
                    sms.Send("您訂購的充氣女友已發貨,查收後請愛惜使用", c.Key, c.Value, tpl);
                }
            }
        }

小黃修改了代碼,跟測試保證以後不用動業務那些代碼,以後測試只測試一下實現模塊就OK了,上線運行後,測試邪惡的笑了一下撿起了地上的肥皂 (逃

 

好了,經過上面純屬虛構的故事,相信大家都對工廠,Ioc和DI等有點理解了

DI (Dependency Injection,依賴注入)

一看這名字不百度都不知是什麼意思,那我們再先看一下相關的概念吧

依賴倒置原則(Dependence Inversion Principle,簡稱DIP)

該死,又多一個概念,引入這個概念的話看來又要引入另一個概念了:解耦

什麼是解耦,解耦字面的意思也很明白了解除耦合,解除軟件各個模塊之間的耦合,讓一個模塊不要依賴另一個模塊

在上面的案例裡,解耦就是解除了業務代碼對各個SMS模塊的耦,業務跟具體實現的模塊完全沒有依賴,第一版中,業務是編碼階段就跟各SMS類耦合了,嚴重依賴SMS類,依賴在編碼階段

抽象出接口,然後使用了Ioc後這個依賴在編碼階段是沒有的,依賴調用實現的SMS的發生在系統運行的階段,依賴在運行時,Ioc把這種依賴完全反轉過來了,耦合解除了,依賴倒置了

IoC (Inversion of Control,控制反轉)

Ioc的概念也就跟依賴倒置差不多,是控制權的轉移,原先由業務代碼決定用那個SMS模塊的轉移到由Ioc容器根據配置文件決定使用什麼SMS模塊,控制權轉移到Ioc框架上.

Ioc思想實現的Ioc容器就是負責按配置文件注入依賴(DI)的模塊到容器裡返回給業務調用

簡單說,Ioc容器是就一個豪華版工廠,自動化裝配的工廠,工廠知道是什麼吧,就是我把產品規格發給你,你把產品給我,我不管你給的是怎麼制作怎麼實現的產品,用什麼材料我一概不管,我只要合我規格的就行。

Ioc就是這樣的一個工廠,我們在業務代碼裡調用Ioc容器(工廠),把需求(產品規格)發給Ioc容器,容器返回實現類(產品)給業務,業務就可以按這個規格來草作這個產品了

Ioc容器就是負責注入依賴的模塊

粗淺見解,打臉輕點


造輪子目標

環境當然是.net core下了

支持配置文件

支持注冊單例

支持延遲加載

我不是標題黨,用.net魔法真的可以是直接new的速度,當然這個多了一層調用,肯定會下降一丁點,但速度比反射型的不知高那裡去了

 


 待續,不會太監,我不是給大魚打廣告

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved