首先,我不是一個開發者,只是業餘學習者。其次我的文化水平很低,寫這個主要是記錄一下當前對於這塊的理解,因為對於一個低水平 的業餘學習者來說,忘記是很平常的事,因為接觸、應用的少,現在理解,可能過段時間就會忘了,自己記錄下來應該可以少走些彎路,以免從頭再來查找資料。 另外,如果我的記錄能幫忙到一些朋友 ...
首先,我不是一個開發者,只是業餘學習者。其次我的文化水平很低,寫這個主要是記錄一下當前對於這塊的理解,因為對於一個低水平 的業餘學習者來說,忘記是很平常的事,因為接觸、應用的少,現在理解,可能過段時間就會忘了,自己記錄下來應該可以少走些彎路,以免從頭再來查找資料。
另外,如果我的記錄能幫忙到一些朋友的話,也難免會有些小滿足的。學習的門檻除了理解能力,絕大部分來自於英文水平和專業術語,我希望的是我能用比較通俗易懂的表達,獲得大家的理解,更希望大牛們是如是做的,所以寫這個更希望的是能得到大牛的幫助。
下麵是我對依賴註入在應用場景的理解
從框架設計方面 你希望的是你設計的架構能有不同的技術(或者叫第三方組件)實現,你只需要通過在固定地方修改極少的代碼便可以。
來個例子吧,假如現在有兩個不同的技術,當然它們應該是在不同的程式集中的,只是為了方便把它們放在一起
/// <summary> /// 第三方組件A /// </summary> public class ModuleA { public void Say() { Console.WriteLine("it is ModuleA"); } } /// <summary> /// 第三方組件B /// </summary> public class ModuleB { public void Say() { Console.WriteLine("it is ModuleB"); } }
你希望你能應用任意其中一個,並且可以隨意更改。你可以對外抽象出一個約束介面或者抽象類,下麵我用的是介面,你可以在你的設計中,通過構造函數來引用
/// <summary> /// 介面 /// </summary> public interface IInterface { void Say();//介面約束必須實現這個方法 } public class BaseStyle { private IInterface module;//你並不知道也不關心當前用的是哪個組件,只要組件能實現這個介面就行 public BaseStyle(IInterface module)//能過構造函數引用介面 { this.module = module; } public void Say() { module.Say(); } }
通過屬性引用
/// <summary> /// 通過屬性引用組件方式 /// </summary> public class BaseStyle { public IInterface Module { get; set; } public void Say() { Module.Say(); } }
當你要應用任意一個組件時,只須要讓它實現介面
public class ModuleA : IInterface { public void Say() { Console.WriteLine("it is ModuleA"); } }
這樣就把BaseStyle類的Say方法分離出來,通過介面的實現類來完成。相當於介面的實現類可以是任意類。這便是傳說中的介面分離,低藕合思想。例子不需要大,理解才是最重要。往大了說,這就相當於是面向介面編程。這是架構設計方面 。
然後就是應用了,也就是註入工作,Autofac之類的依賴註入組件不是必須的,只是當你的程式大了,用這些封裝好的組件會更方便,易維護。你可以new BaseStyle然後傳入對就的參數或設置屬性就行了。
我一般用的是Autofac,用autofac,代碼應該 是這樣的
class Program { static void Main(string[] args) { ContainerBuilder builder = new ContainerBuilder(); builder.Register(o => new BaseStyle(o.Resolve<IInterface>()));//實例註入 builder.RegisterType<ModuleA>().As<IInterface>(); //類型註入 using (var container = builder.Build()) { BaseStyle bs = container.Resolve<BaseStyle>(); bs.Say(); } Console.ReadKey(); }
當然這樣用肯定是沒必要用autofac的,當有很多類須要用到Autofac註入時,這樣並不會減少代碼量,當要修改時,每個地方都要修改,如果只對Autofac停留在這個層面,還是不必要用它的好。
我比較喜歡的是封裝一個Container類,由於我一慣喜歡用強類型,所以沒有試過通過配置文件來配置。可能是因為我沒有接觸到真實項目的原因吧
封裝一個類
public class ServiceContainer { private ServiceContainer() { }//不對外提供構造函數 private static IContainer container; /// <summary> /// 初始化container /// </summary> /// <param name="action"></param> public static void Initialize(Action<ContainerBuilder> action) { if(action!=null) { ContainerBuilder builder = new ContainerBuilder(); action(builder); container = builder.Build(); } } /// <summary> /// 封裝一個不帶參數的IContainer的方法 /// </summary> /// <typeparam name="Tservice"></typeparam> /// <returns>返回一個註入的服務類實例</returns> public static Tservice Resolve<Tservice>() { ThrowIfNotInitialized(); //檢測容器是否初始化 return container.Resolve<Tservice>(); } //此處省略其它IContainer方法的封裝 /// <summary> /// 檢測容器是否初始化 /// </summary> private static void ThrowIfNotInitialized() { if (container == null) throw new InvalidOperationException("請先初始化容器,調用Initialize()方法"); } }
通過個類提供的靜態初始化方法,可以在應用程式開始處,將所有要註入的依賴註入到容器中,然後你可以在程式的任意地方通過靜態方法Resolve<T>方法從容器中取得你想要的具體依賴。這裡我只封裝了一個方法,還可以封裝更多的靜態方法,靈活調用。
最後再傳上測試的所有代碼。這裡我抽象出了一些基類,設置好依賴註入,方便客戶端調用時不需要傳入參數或是設置屬性,個人感覺應該比較完美了
using System; using Autofac; namespace AutofacDemo { class Program { static void Main(string[] args) { ServiceContainer.Initialize(o => { o.RegisterType<ModuleA>().As<IInterface>(); //o.RegisterType<ModuleB>().As<IInterface>();需要用其它實現時,只須要修改傳入對應的類型 }); StyleA a = new StyleA();//純凈的客戶端調用 StyleB b = new StyleB(); a.Say(); b.Say(); #region 基本註入 //ServiceContainer.Initialize(o => // { // o.RegisterType<ModuleA>().As<IInterface>(); // }); //StyleA a = new StyleA(ServiceContainer.Resolve<IInterface>()); //StyleB b = new StyleB(); //b.Module = ServiceContainer.Resolve<IInterface>(); //a.Say(); //b.Say(); #endregion #region 實例註入、屬性註入 //ServiceContainer.Initialize(o => //{ // o.Register(c => new StyleA(c.Resolve<IInterface>())); //實例註入 // o.Register(c => new StyleB { Module = c.Resolve<IInterface>() });//屬性註入 // o.RegisterType<ModuleA>().As<IInterface>(); //}); //StyleA a = ServiceContainer.Resolve<StyleA>(); //StyleB b = ServiceContainer.Resolve<StyleB>(); //a.Say(); b.Say(); #endregion Console.ReadKey(); } } /// <summary> /// 介面 /// </summary> public interface IInterface { void Say(); } /// <summary> /// 介面的某個實現類A,通常相當於第三方組件 /// </summary> public class ModuleA : IInterface { public void Say() { Console.WriteLine("it is ModuleA"); } } /// <summary> /// 介面的某個實現類B,通常相當於第三方組件 /// </summary> public class ModuleB : IInterface { public void Say() { Console.WriteLine("it is ModuleB"); } } public class BaseStyle { private IInterface module; public BaseStyle(IInterface module) { this.module = module; } public void Say() { module.Say(); } } /// <summary> /// 基類通過構造函數引用組件方式 /// </summary> public abstract class BaseStyleA { private IInterface module; protected BaseStyleA(IInterface module) { this.module = module; } public void Say() { module.Say(); } } /// <summary> /// 基類通過屬性引用組件方式 /// </summary> public abstract class BaseStyleB { public virtual IInterface Module { get; set; } public void Say() { Module.Say(); } } public class StyleA:BaseStyleA { public StyleA(IInterface module) : base(module) { } public StyleA() : base(ServiceContainer.Resolve<IInterface>())//方便客戶端調用時不需要傳參 { } } public class StyleB:BaseStyleB { public override IInterface Module { get { return ServiceContainer.Resolve<IInterface>();//方便客戶端調用時不需要設置屬性 } } } }View Code