.net core正式版前兩天發布了,喜大普奔,借此機會,強行來寫第一篇博客
第一次寫博客,有點緊張,不知怎麼才能裝做經常寫的樣子(:
第一次,造個小輪子吧,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的速度,當然這個多了一層調用,肯定會下降一丁點,但速度比反射型的不知高那裡去了
待續,不會太監,我不是給大魚打廣告