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中。
- 建構函式注入:如果物件A在建構函式中依賴於物件B,物件B在建構函式中依賴於物件C,如果要構造物件A,自動構造C,傳入物件B的建構函式,構造出物件B,傳入物件A,構造出物件A;
- 屬性注入:如果物件A在屬性中依賴於物件B,物件B在屬性中依賴於物件C,如果要構造物件A,在構造出物件A之後,檢測屬性,如果屬性有依賴,就自動的去建立依賴的物件,建立出來以後,賦值給物件A中共的屬性; 如果在建立的過程中,還有依賴,就繼續建立
- 方法注入:如果物件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>();