IOC容器和依賴注入

一纸年华發表於2024-08-16

1 依賴倒置

依賴倒置的核心價值:
如果沒有依賴倒置,全部都是依賴細節,如果分層架構是 A層---B層--C層---D層---E層---F層,下層的修改,可能會導致上層隨之改變,F層如果改變,E層要改,D層要改,C層要改......影響很大,成水波式向上影響,架構就的極度不穩定。
如果都是依賴於抽象的,抽象即介面或抽象類。 抽象是相對穩定的,修改下層不會影響上層。因為上層不是依賴於具體的,這讓我們的程式碼更加穩定。
IOC 控制物件的建立,不就是完全控制了物件的建立嗎?所以,IOC的本質是工廠。
既然IOC是工廠,那麼工廠生產產品就是建立物件。

2 簡單工廠建立物件會不會有什麼問題呢?

工廠建立物件,如果物件沒有無引數建構函式,必須得按照建構函式引數的要求,準備好建構函式。那就會出現問題:如果物件存在層層依賴,有多少層依賴,就得準備多少個方法。
要解決這個問題,我們就有了這樣的一個目標:如果有依賴的,能夠做到,在建立物件的時候,能夠自動把物件依賴的物件給建立出來,然後傳遞進去,這樣就很好。
Asp.Net Core的內建IOC容器可以幫助我們解決掉這個問題。

3 內建IOC容器

需要引入Microsoft.Extensions.DependencyInjection包。
Zlt.IOC.Interfaces 名稱空間下建立介面:

public interface IHeadphone
{
}
public interface IMicrophone
{
}   
public interface IPower
{
}
public interface IPhone
{
    void Call();
    void Init123456678890(IPower iPower);
    IMicrophone Microphone { get; set; }
    IHeadphone Headphone { get; set; }
    IPower Power { get; set; }
}

Zhaoxi.IOC.Services 實現介面:

    public class Headphone : IHeadphone
    { 
        public Headphone()
        {
            Console.WriteLine($"{this.GetType().Name}被構造。。");
        } 
    }
    public class Headphone : IHeadphone
    {
        public Headphone()
        {
            Console.WriteLine($"{GetType().Name}被構造。。");
        }
    }
    public class Microphone : IMicrophone
    {
        public IHeadphone _IHeadphone { get; set; }

        public IHeadphone _IHeadphoneField;

        public void SetHeadphone(IHeadphone headphone)
        {
            _IHeadphoneField = headphone;
        }

        /// <summary>
        /// 沒有無引數建構函式
        /// </summary>
        /// <param name="headphone"></param>
        [SelectCtor]
        public Microphone(IHeadphone headphone)
        {
            Console.WriteLine($"{GetType().Name}被構造。。");
        }

        public Microphone(IHeadphone headphone, IHeadphone headphone1)
        {
            Console.WriteLine($"{GetType().Name}被構造。。");
        }
    }
    public class Power : IPower
    {
        public Power(IMicrophone microphone)
        {
            Console.WriteLine($"{GetType().Name}被構造。。");
        }
    }
    public class AndroidPhone : IPhone
    {
        public IMicrophone Microphone { get; set; }
        public IHeadphone Headphone { get; set; }
        public IPower Power { get; set; }

        public AndroidPhone(IPower Power)
        {
            Power = Power;
            Console.WriteLine("{0}建構函式", GetType().Name);
        }

        public void Call()
        {
            Console.WriteLine("{0}打電話", GetType().Name); ;
        }

        public void Init123456678890(IPower iPower)
        {
            Power = iPower;
        }
    }

呼叫:

//1.IOC容器的例項
ServiceCollection services = new();
//2.註冊抽象和具體之間的關係
services.AddTransient<IHeadphone, Headphone>();
services.AddTransient<IMicrophone, Microphone>();
services.AddTransient<IPower, Power>();
services.AddTransient<IPhone, AndroidPhone>();
//3.Buidl下得到一個Provider
ServiceProvider serviceProvider = services.BuildServiceProvider();
//4.建立物件
IHeadphone headphone = serviceProvider.GetService<IHeadphone>();
IMicrophone microphone = serviceProvider.GetService<IMicrophone>();
IPower power = serviceProvider.GetService<IPower>();
IPhone iphone = serviceProvider.GetService<IPhone>();

這就是框架的依賴注入,如果物件A依賴於物件B,物件B依賴於物件C,在建立物件A時,自動先建立物件C,滿足物件B的依賴,再建立物件B,滿足A的依賴,最後建立出物件A。

4 依賴注入DI

內建容器中,僅支援建構函式注入這一種。依賴注入有3中。

  1. 建構函式注入:如果物件A在建構函式中依賴於物件B,物件B在建構函式中依賴於物件C,如果要構造物件A,自動構造C,傳入物件B的建構函式,構造出物件B,傳入物件A,構造出物件A;
  2. 屬性注入:如果物件A在屬性中依賴於物件B,物件B在屬性中依賴於物件C,如果要構造物件A,在構造出物件A之後,檢測屬性,如果屬性有依賴,就自動的去建立依賴的物件,建立出來以後,賦值給物件A中共的屬性; 如果在建立的過程中,還有依賴,就繼續建立
  3. 方法注入:如果物件A在某個方法中依賴於物件B,物件B在某個方法中依賴於物件C,如果要構造物件A,在構造出物件A之後,檢測某個方法,如果某個方法的引數有依賴,就自動的去建立某個方法引數依賴的物件,建立出來以後,去執行這個方法,把構造出來的物件傳遞給這方法的引數; 如果在建立的過程中,還有方法依賴,就繼續建立。

5 這個 IOC 和依賴注入 DI 之間,是什麼關係?

IOC 是架構設計的一種方案,一種目標;
DI 是實現 IOC 容器的的時候,一種解決依賴問題的技術手段;

6 底層究竟如何實現?

下面就是關於IOC底層的建構函式注入的核心邏輯。
Zlt.IOC.Common 提供公共方法:

public interface IMyServiceCollection
{
    /// <summary>
    /// 註冊具體和抽象之間的關係
    /// </summary>
    /// <typeparam name="T">抽象</typeparam>
    /// <typeparam name="S">具體</typeparam>
    public void Register<T, S>() where S : T;
    /// <summary>
    /// 獲取物件的例項
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public T GetService<T>();
}
public class MyServiceCollection : IMyServiceCollection
{
    private static Dictionary<string, Type> MapDic = new Dictionary<string, Type>();
    /// <summary>
    ///  儲存  註冊具體和抽象之間的關係
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="S"></typeparam>
    public void Register<T, S>() where S : T
    {
        MapDic.Add(typeof(T).FullName, typeof(S));
    }
    /// <summary>
    ///  獲取物件的例項
    /// </summary>
    /// <typeparam name="T">引數:必然是抽象</typeparam>
    /// <returns></returns>
    public T GetService<T>()
    {
        string abstractFullName = typeof(T).FullName;
        Type type = MapDic[abstractFullName];
        return (T)CreateInstance(type);
    }
    /// <summary>
    /// 建立物件 有依賴的遞迴建立
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public object CreateInstance(Type type)
    {
        ConstructorInfo? ctor = type.GetConstructors()
            .Where(a => a.IsDefined(typeof(SelectCtorAttribute)))
            .FirstOrDefault();
        if (ctor == null)
        {
            ctor = type.GetConstructors()
                .OrderByDescending(c => c.GetParameters().Length)
                .First();
        }
        List<object> parametList = new();
        foreach (var ctorParameter in ctor.GetParameters())
        {
            Type tageType = MapDic[ctorParameter.ParameterType.FullName];
            object oPrarmeter = CreateInstance(tageType);
            parametList.Add(oPrarmeter);
        }
        return Activator.CreateInstance(type, parametList.ToArray());
    }
}
[AttributeUsage(AttributeTargets.Constructor)]
public class SelectCtorAttribute : Attribute
{
}

呼叫:

IMyServiceCollection myServices = new MyServiceCollection();
myServices.Register<IHeadphone, Headphone>();
myServices.Register<IMicrophone, Microphone>();
myServices.Register<IPower, Power>();
myServices.Register<IPhone, AndroidPhone>();

IHeadphone headphone1 = myServices.GetService<IHeadphone>();
IMicrophone microphone1 = myServices.GetService<IMicrophone>();
IPower power1 = myServices.GetService<IPower>();
IPhone iphone1 = myServices.GetService<IPhone>();

相關文章