1. 程式人生 > 實用技巧 >解決“客戶機作業系統已禁用CPU”的問題

解決“客戶機作業系統已禁用CPU”的問題

目錄

前言

文中內容是結合多篇文章的學習筆記,記錄下來以免時間久了忘記。

面向物件設計(OOD)有助於我們開發出高效能、易擴充套件以及易複用的程式。其中,OOD有一個重要的思想那就是依賴倒置原則(DIP),並由此引申出IOC、DI以及IOC容器等概念。通過本文我們將一起學習這些概念,並理清他們之間微妙的關係。

概念

依賴倒置原則(DIP):一種軟體架構設計的原則(抽象概念),依賴抽象而不是依賴細節。

控制反轉(IOC):一種反轉流、依賴和介面的方式(DIP的具體實現方式),通過抽象獲取細節,實現這個動作的時候就是控制反轉。

依賴注入(DI):IOC的一種實現方式,用來反轉依賴(IOC的具體實現方式),DI是實現IOC的一種技術手段。

IOC容器:依賴注入的框架,用來對映依賴,管理物件建立和生存週期(DI框架),IOC容器實際上是一個DI框架,它能簡化我們的工作量。它包含以下幾個功能:

  • 動態建立、注入依賴物件。
  • 管理物件生命週期。
  • 對映依賴關係。

圖示

依賴

以三層架構為例,從上到下,UI->BLL->DAL這就是依賴,這種最基本的依賴是依賴於細節的,耦合度較高,DAL如果修改了程式碼,則底層到高層的程式碼都需要進行改變。所以需要進行解耦,由此引出依賴倒置原則(DIP)。

依賴倒置DIP

耦合關係就是依賴關係,如果依賴關係相當繁雜,牽一髮而動全身,很難維護;依賴關係越少,耦合關係就越低,系統就越穩定,所以我們要減少依賴。

幸虧Robert Martin大師提出了面向物件設計原則----依賴倒置原則:   

  • A. 上層模組不應該依賴於下層模組,它們共同依賴於一個抽象。  
  • B. 抽象不能依賴於具象,具象依賴於抽象。

理解:A.上層是使用者,下層是被使用者,這就導致的結果是上層依賴下層了,下層變動了,自然就會影響到上層了,導致系統不穩定,甚至是牽一髮而動全身。那怎麼減少依賴呢?就是上層和下層都去依賴另一個抽象,這個抽象比較穩定,整個就來說就比較穩定了。

B.面向物件程式設計時面向抽象或者面向藉口程式設計,抽象一般比較穩定,實現抽象的具體肯定是要依賴抽象的,抽象不應該去依賴別的具體,應該依賴抽象。

依賴倒置原則,它轉換了依賴,高層模組不依賴於低層模組的實現,而低層模組依賴於高層模組定義的介面

。通俗的講,就是高層模組定義介面,低層模組負責實現。

Bob Martins對DIP的定義:

高層模組不應依賴於低層模組,兩者應該依賴於抽象。

抽象不不應該依賴於實現,實現應該依賴於抽象。

控制反轉IOC

DIP是一種 軟體設計原則,它僅僅告訴你兩個模組之間應該如何依賴,但是它並沒有告訴如何做。IOC則是一種 軟體設計模式,它告訴你應該如何做,來解除相互依賴模組的耦合。控制反轉(IOC),它為相互依賴的元件提供抽象,將依賴(低層模組)物件的獲得交給第三方(系統)來控制即依賴物件不在被依賴模組的類中直接通過new來獲取

控制反轉IOC是Inversion of Control的縮寫,是說物件的控制權進行轉移,轉移到第三方,比如轉移交給了IOC容器,它就是一個建立工廠,你要什麼物件,它就給你什麼物件,有了IOC容器,依賴關係就變了,原先的依賴關係就沒了,它們都依賴IOC容器了,通過IOC容器來建立它們之間的關係。

軟體設計原則:原則為我們提供指南,它告訴我們什麼是對的,什麼是錯的。它不會告訴我們如何解決問題。它僅僅給出一些準則,以便我們可以設計好的軟體,避免不良的設計。一些常見的原則,比如DRY、OCP、DIP等。

軟體設計模式:模式是在軟體開發過程中總結得出的一些可重用的解決方案,它能解決一些實際的問題。一些常見的模式,比如工廠模式、單例模式等等。

依賴注入(DI)

上面說到控制反轉,是一個思想概念,但是也要具體實現的,上面的配置檔案也是一種實現方式。依賴注入提出了具體的思想。

依賴注入DI是Dependency Injection縮寫,它提出了“哪些東東的控制權被反轉了,被轉移了?”,它也給出了答案:“依賴物件的建立獲得被反轉”。

所謂依賴注入,就是由IoC容器在執行期間,動態地將某種依賴關係注入到物件之中。

依賴注入(DI),它提供一種機制,將需要依賴(低層模組)物件的引用傳遞給被依賴(高層模組)物件。

IOC容器

IOC中最基本的技術就是“反射(Reflection)”程式設計,通俗來講就是根據給出的類名(字串方式)來動態地生成物件。這種程式設計方式可以讓物件在生成時才決定到底是哪一種物件。反射的應用是很廣泛的,很多的成熟的框架,比如象Java中的hibernate、Spring框架,.Net中 NHibernate、Spring.NET框架都是把“反射”做為最基本的技術手段。

我們可以把IOC容器的工作模式看做是工廠模式的昇華,可以把IOC容器看作是一個工廠,這個工廠裡要生產的物件都在配置檔案中給出定義,然後利用程式語言的的反射程式設計,根據配置檔案中給出的類名生成相應的物件。從實現來看,IOC是把以前在工廠方法裡寫死的物件生成程式碼,改變為由配置檔案來定義,也就是把工廠和物件生成這兩者獨立分隔開來,目的就是提高靈活性和可維護性。

動手實現一個無限層級的IOC容器

IFishServiceCollection

public interface IFishServiceCollection
{
    /// <summary>
    /// 新增具有實現的在serviceType中指定的型別的臨時服務
    /// 在ImplementationType中指定的型別為指定的Microsoft.Extensions.DependencyInjection.IServiceCollection。
    /// </summary>
    /// <param name="serviceType">要註冊的服務的型別</param>
    /// <param name="implementationType">服務的實現型別</param>
    /// <param name="shortName">服務的實現類型別名</param>
    void AddTransient(Type serviceType,Type implementationType,string shortName =null);
    /// <summary>
    /// 獲取指定型別的服務物件
    /// </summary>
    /// <typeparam name="T">要獲取的服務型別</typeparam>
    /// <returns>生產的服務</returns>
    T GetService<T>(string shortName=null);
}

FishServiceCollection

public class FishServiceCollection : IFishServiceCollection
    {

        private Dictionary<string, Type> richarDictionary = new Dictionary<string, Type>();

        //拼接要註冊的服務的型別和別名
        private string GetKey(Type type, string shortName) => $"{type.FullName}___{shortName}";
        //{
        //    return $"{type.FullName}___{shortName}";
        //}

        /// <summary>
        /// 註冊服務--其實就是把抽象和具體的對映關係儲存起來
        /// </summary>
        /// <param name="serviceType">要註冊的服務的型別(抽象:介面等)</param>
        /// <param name="implementationType">服務的實現型別(細節:例項)</param>
        /// <param name="shortName">別名</param>
        public void AddTransient(Type serviceType, Type implementationType,string shortName)
        {
            //使用別名完成多實現功能
            string Key = GetKey(serviceType,shortName);
            richarDictionary.Add(Key, implementationType);
            //01 將物件與細節儲存在字典內
            //richarDictionary.Add(serviceType.FullName,implementationType);
        }
        /// <summary>
        /// 獲取服務例項
        /// </summary>
        /// <typeparam name="T">要獲取的服務型別(抽象:介面)</typeparam>
        /// <returns>生產的服務</returns>
        public T GetService<T>(string shortName)
        {
            #region 第一版 簡單層級
            ////02 通過字典取出具體的型別
            //Type type = richarDictionary[typeof(T).FullName];
            ////03 通過反射建立例項
            ////return (T)Activator.CreateInstance(type);//如果需要構造的物件中建構函式引數 不為空;就報錯了 呼叫的無引數的建構函式

            ////1.先取構造 當前類建構函式中需要的引數的物件   確定構造當前物件使用那個建構函式(預設選擇引數最多的建構函式)
            //var contrArray = type.GetConstructors();//獲取Type的所有建構函式
            ////2.根據建構函式引數數量降序配列,再取第一個==獲取到建構函式引數最多的這一個
            //var ctor = contrArray.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
            ////3.準備建構函式的引數
            //List<object> prarlist = new List<object>();
            //foreach (ParameterInfo para in ctor.GetParameters())
            //{
            //    Type paraType = para.ParameterType;
            //    Type parTargetType = richarDictionary[paraType.FullName];
            //    #region 思路引導
            //    //1* 確定使用那個建構函式
            //    //var contrArray1 = parTargetType.GetConstructors();
            //    //2* 根據建構函式引數數量降序排序,再取第一個==獲取到建構函式引數最多的這一個
            //    //var ctor1 = contrArray.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
            //    //3* 準備建構函式的引數········
            //    //不知道要構造的物件的建構函式具體依賴多少個層級;
            //    //永遠寫不完
            //    //怎麼辦?使用遞迴
            //    #endregion
            //    var target = Activator.CreateInstance(parTargetType);
            //    prarlist.Add(target);
            //}
            ////4.帶引數的構造當前物件
            //return (T)Activator.CreateInstance(type, prarlist.ToArray());
            #endregion
            #region 多層級
            Type type = richarDictionary[GetKey(typeof(T), shortName)];
            return (T)this.GetService(type);
            #endregion
        }

        private object GetService(Type type)
        {
            #region 建構函式注入
            //1.先去構造 當前類建構函式中需要的引數的物件   確定構造當前物件使用那個建構函式(預設選擇引數最多的建構函式)
            var contrArray = type.GetConstructors();//獲取Type的所有建構函式
            //2.根據建構函式引數數量降序配列,再取第一個==獲取到建構函式引數最多的這一個
            //2.1 這是預設情況下,選擇建構函式引數最多的,但是有特殊情況,如果就需要選擇一個自己指定的建構函式呢?
            //2.2 需求:需要通過自己指定選擇不同的建構函式,如果沒有指定,就選擇預設
            //2.3 如何解決呢?=>通過特性解決
            //2.4 通過特性支援,可以在建構函式上,標記一個特性,在選擇建構函式的時候,就判斷,如果標記有特性,就選擇標記有特性的這個建構函式,如果沒有特性,就選擇預設最多的建構函式
            //2.4.1 找標記的有特性的建構函式
            ConstructorInfo ctor = contrArray.Where(a => a.IsDefined(typeof(SelectConstructorAttribute))).FirstOrDefault();
            if (ctor == null)
            {
                ctor = contrArray.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
            }

            //3.準備建構函式的引數
            List<object> prarlist = new List<object>();
            foreach (ParameterInfo para in ctor.GetParameters())
            {
                Type paraType = para.ParameterType;

                string shortName = GetKey(paraType,null);

                Type parTargetType = richarDictionary[shortName];
                //var target = Activator.CreateInstance(parTargetType);

                var target = this.GetService(parTargetType);//遞迴
                prarlist.Add(target);
            }
            #endregion
            #region 物件注入
            //構造物件
            object oInstance = Activator.CreateInstance(type, prarlist.ToArray());
            #endregion
            #region 屬性注入
            //找出物件中需要做注入的屬性,然後例項化屬性對應型別的物件,賦值給屬性
            //1.找出所有的屬性;
            //2.找出指定標識,按照有標識的去給屬性做注入 標識--Attribute

            //拿出所有屬性,使用特性判斷需要注入的屬性
            var attArray = type.GetProperties().Where(p=>p.IsDefined(typeof(PropertyInjeictionAttribute),true));
            foreach (var prop in attArray)
            {
                //找到具體型別
                Type propType = prop.PropertyType;
                string shortName = GetKey(propType,null);
                Type propImType = richarDictionary[shortName];
                //構造
                object propInstance = GetService(propImType);
                prop.SetValue(oInstance,propInstance);
            }
            #endregion
            #region 方法注入
            foreach (var method in type.GetMethods().Where(p => p.IsDefined(typeof(MethodImplAttributes), true)))
            {
                List<object> paraInjectionList = new List<object>();
                foreach (var para in method.GetParameters())
                {
                    Type paraType = para.ParameterType;
                    string shortName = GetKey(paraType, null);
                    Type paraImType = richarDictionary[shortName];
                    object mInstance = GetService(paraImType);
                    paraInjectionList.Add(mInstance);
                }
                method.Invoke(oInstance,paraInjectionList.ToArray());
            }
            #endregion
            //多實現注入--一個介面多實現後,希望在都註冊以後,能夠選擇性的獲取具體的例項
            //1.當然要標識一些,一個介面的兩個實現在註冊時候必然要標識下
            //2.在獲取的時候,就可以通過這個標識來構造對應的物件
            //3.起個別名
            return oInstance;
        }
    }

MethodInjeictionAttribute

[AttributeUsage(AttributeTargets.Method)]
public class MethodInjeictionAttribute:Attribute
{
}

PropertyInjeictionAttribute

[AttributeUsage(AttributeTargets.Property)]
public class PropertyInjeictionAttribute:Attribute
{
}

SelectConstructorAttribute

[AttributeUsage(AttributeTargets.Constructor)]
public class SelectConstructorAttribute:Attribute
{
}

相關文章

IoC模式(依賴、依賴倒置、依賴注入、控制反轉)

深入理解DIP、IoC、DI以及IoC容器