<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 原則22:選擇定義并實現接口,而不是基類 **By D.S.Qiu** **尊重他人的勞動,支持原創,轉載請注明出處:[http://dsqiu.iteye.com](http://dsqiu.iteye.com)** 抽象基類提供的是類繼承結構的公共祖先。接口描述實現類的原子級功能。兩者都更有千秋,卻不盡相同。接口是一種合約式的設計:實現接口的類必須提供所有期望函數的實現。抽象基類提供一組相關類的共有抽象。這是老套的,它是這樣的:繼承是“ is a ”的關系,接口是“ behavies like ”的關系。這些陳詞濫調已經說了很久了,因為它們的結構說明了彼此的不同:基類描述的是對象是什么,接口描述的是對象的表現方式。 接口描述的是一個功能集,更像是一個合約。 你可以在接口里創建任何占位符:方法,屬性,索引器和事件。實現接口的類必須提供接口定義的所有元素的具體實現。你必須實現所有的方法,提供所有屬性的訪問器和索引器和定義所有事件。你確定并提前相同的行為到接口中。你可以使用接口作為參數和返回值。你還可以有更多機會重用代碼因為不相關類可以實現同一個接口。更重要的是,其他開發者實現接口比繼承基類會更容易。 你不能在接口做的是不能提供任何成員變量。接口沒有任何實現,并且它們不能包含任何具體的數據成員。你的類要么全部實現接口的定義的所有元素,要么就沒有實現接口。當然,你可以通過創建擴展方法讓人覺得接口實現的錯覺。System.Linq.Enumerable 類包含對于30個聲明在 IEnumerable&lt;T&gt; 的擴展方法。擴展方法是實現 IEnumerable&lt;T&gt; 類的一劑良藥。你可以查看原則8: ``` public static class Extensions { public static void ForAll<T>( this IEnumerable<T> sequence, Action<T> action) { foreach (T item in sequence) action(item); } } // usage foo.ForAll((n) => Console.WriteLine(n.ToString())); ``` 抽象基類可以提供給子類一些實現,可以描述一些共有的行為。你可以指定數據成員,具體方法,實現虛函數,屬性,事件和索引器。基類可以可以通過方法實現共有可重用功能。任何元素都可以是 virtual , abstract 或 nonvirtual 。抽象基類可以通過具體行為,而接口不行。 實現重用功能還有一個好處:如果你在基類添加方法,所有的子類都自動隱式的加強。在這個意義上,隨著時間的推移,,基類提供了一種高效的方式來擴展幾個類的行為:基類增加和實現功能,子類就立即合并這些行為。在接口中添加添加元素會破壞所有實現這個接口的類。它們沒有包含這個元素的實現就不能通過編譯。每個實現類都要更新以包含這個新元素。 如何選擇抽象基類和接口其實就是一個隨著時間的推移如何更好的支持你的抽象的功能的問題。接口是固定的,你發布的接口就是功能集的一個所有實現類要遵循的合約。基類可以隨著時間推移而擴展。這些擴展會變成每個子類的一部分。 這兩個模型可以混合復用的實現代碼同時支持多個接口。.NET 框架很明顯的例子就是 IEnumerable&lt;T&gt; 接口和 System.Linq.Enumerable 類。System.Linq.Enumerable 類包含大量定義在 System.Collection.Generic.IEnumerable&lt;T&gt; 接口中的擴展方法。這個分離有很重要的意義。任何類實現 IEnumerable&lt;T&gt; 直接就包含這些擴展方法。并且,還有額外一些沒有在 IEnumerable&lt;T&gt; 定義的方法。這意味著開發者沒有必要自己去實現這些方法。 檢查實現 IEnumerable&lt;T&gt; 的天氣觀察類。 ``` public enum Direction { North, NorthEast, East, SouthEast, South, SouthWest, West, NorthWest } public class WeatherData { public double Temperature { get; set; } public int WindSpeed { get; set; } public Direction WindDirection { get; set; } public override string ToString() { return string.Format("Temperature = {0}, Wind is {1} mph from the {2}",Temperature, WindSpeed, WindDirection); } } public class WeatherDataStream : IEnumerable<WeatherData> { private Random generator = new Random(); public WeatherDataStream(string location) { // elided } private IEnumerator<WeatherData> getElements() { // Real implementation would read from // a weather station. for (int i = 0; i < 100; i++) yield return new WeatherData { Temperature = generator.NextDouble() * 90, WindSpeed = generator.Next(70), WindDirection = (Direction)generator.Next(7) }; } #region IEnumerable<WeatherData> Members public IEnumerator<WeatherData> GetEnumerator() { return getElements(); } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return getElements(); } #endregion } ``` WeatherStream 類要模擬出一系列的天氣觀察。為了實現這點它實現了 IEnumerable&lt;WetherData&gt; 。這就得實現兩個方法:GetEnumerator&lt;T&gt; 方法和類的 GetEnumerator 方法。后者的顯示實現可以讓客戶端代碼自然把泛型對象向上轉換為 System.Object 。 實現了這兩個方法 WeatherStream 類支持所有在 System.Linq.Enumerable 的擴展方法。這意味著 WeatherStream 可以是 LINQ 查詢的數據源: var warmDays = from item in new WeatherDataStream("Ann Arbor") where item.Temperature &gt; 80 select item; LINQ 查詢語法會被編譯成方法調用。上面查詢會被翻譯為下面的調用: var warmDays2 = new WeatherDataStream("Ann Arbor").Where(item =&gt; item.Temperature &gt; 80). Select(item =&gt; item); 在上面的代碼,Where 和 Select 的調用看起來覺得它們是 IEnumerable&lt;WeatherData&gt; 的方法。但其實不是。這兩個方法像是屬于 IEnumerable&lt;WeatherData&gt; 因為它們是擴展方法。它們實際是 System.Linq.Enumerable 的靜態方法。編譯器翻譯這些調用為下面的靜態調用: ``` // Don't write this, for explanatory purposes var warmDays3 = Enumerable.Select(Enumerable.Where( new WeatherDataStream("Ann Arbor"), item => item.Temperature > 80), item => item); ``` 最后的這個版本是告訴你接口真的不可以包含實現。你通過使用擴展方法來模擬。 LINQ 就是在 System.Linq.Enumerable 類中創建 IEnumerable&lt;T&gt; 的擴展方法。 這讓我回到使用接口左右參數和返回值的主題。接口可以任意不相關的類實現。接口的編碼比基類的編碼給其他開發者提供了更多的靈活性。這樣非常重要所以 .NET 環境只支持單一繼承關系。 下面三個方法完成的工作是相同的: ``` public static void PrintCollection<T>(IEnumerable<T> collection) { foreach (T o in collection) Console.WriteLine("Collection contains {0}",o.ToString()); } public static void PrintCollection(System.Collections.IEnumerable collection) { foreach (object o in collection) Console.WriteLine("Collection contains {0}",o.ToString()); } public static void PrintCollection(WeatherDataStream collection) { foreach (object o in collection) Console.WriteLine("Collection contains {0}",o.ToString()); } ``` 第一個方法是可重用的,任何實現 IEnumerable&lt;T&gt; 的類都可以使用這個方法。除了 WeahterDataStream ,List&lt;T&gt; , SortedList&lt;T&gt; , 數組 和 LINQ 查詢的結果都可以使用。第二個方法對很多類也有用,但是只是用在不完美的非泛型 IEnumerable 上。最后的方法是最不能重用的。它不能被數組,ArrayList , DataTable , HashTable , ImageList 和其他集合類使用。在方法編碼上使用接口作為參數類是更加普遍和更加容易重用。 使用接口定義類的 API 會更具靈活性。WeatherDataStream 類可以實現返回 WeatherData 對象的集合的方法。那會是像這樣的: ``` public List<WeatherData> DataSequence { get { return sequence; } } private List<WeatherData> sequence = new List<WeatherData>(); ``` 這樣會遺留一個很脆弱的問題。有時,你會將 List&lt;WeatherData&gt; 改為 數組或 SortedList&lt;T&gt; 。任何改變都會破壞之前的代碼。當然,你可以改變參數類型,但也要改變類的 public 接口。改變類的 public 接口會引起大系統的更多改變;你需要在所有訪問 public 屬性的地方進行修改。 第二個問題是更直接和更令人困惑的:List&lt;T&gt; 類提供了改變它所包含的數據的多種方法。這個類的用戶可以刪除,修改甚至替換序列中的每個對象。這些絕大多數都不是你想要的。幸運的是,你可以限制使用這個類的用戶的權限。為了不直接返回內部對象的直接引用,你可以返回接口給你的使用者。這其實就是返回 IEnumerable&lt;WeatherData&gt; 。 當你的類以類暴露屬性,也就暴露了這個類的所有接口。使用接口,你就可以選擇哪些方法和屬性暴露給你的使用者。實現接口的類可以隨著時間推移而改變實現細節。 更重要的是,不相關的類可以實現相同的接口。假設你正在構建一個應用程序管理員工,客戶,和供應商。至少在類的結構是不相關的。但是它們共享了一些共有的功能。它們有名字,它們都要在程序的控制臺展示名字。 ``` public class Employee { public string FirstName { get; set; } public string LastName { get; set; } public string Name { get { return string.Format("{0}, {1}",LastName, FirstName); } } // other details elided. } public class Customer { public string Name { get { return customerName; } } // other details elided private string customerName; } public class Vendor { public string Name { get { return vendorName; } } // other details elided private string vendorName; } ``` Employee , Customer 和 Vendor 類都沒有繼承同一個基類。但是他們分享幾個屬性:名字(上面展示的),地址和聯系電話號碼。你可以提取這些屬性到接口中: ``` public interface IContactInfo { string Name { get; } PhoneNumber PrimaryContact { get; } PhoneNumber Fax { get; } Address PrimaryAddress { get; } } public class Employee : IContactInfo { // implementation elided. } ``` 這個接口通過讓你知道構建這些不相關類的共有的任務來簡化你編程的工作: ``` public void PrintMailingLabel(IContactInfo ic) { // implementation deleted. } ``` 這是對于實現 IContactInfo 所有實體的例行工作。 Customer , Employee 和 Vendor 有相同的工作——但是只是因為你提取它們到接口中了。 使用接口同時衣蛾意味著你可以讓結構體 省去了拆箱的操作帶來的損耗。當你把結構體放入箱中,這個箱可以實現結構體支持的接口。當你通過接口指針訪問結構體,你不需要進行將結構體拆箱成要訪問的那個對象。例如,想象這個接口定義一個鏈接和一個描述: ``` public struct URLInfo : IComparable<URLInfo>, IComparable { private string URL; private string description; #region IComparable<URLInfo> Members public int CompareTo(URLInfo other) { return URL.CompareTo(other.URL); } #endregion #region IComparable Members int IComparable.CompareTo(object obj) { if (obj is URLInfo) { URLInfo other = (URLInfo)obj; return CompareTo(other); } else throw new ArgumentException("Compared object is not URLInfo"); } #endregion } ``` 當你可以很簡單地創建 URLInfo 對象的有序列表因為 URLInfo 實現了 IComparable&lt;T&gt; 和 IComparable 。即使代碼依賴的是類 IComparable 也有更少的 封箱和拆箱次數因為使用者可以調用 IComparable.CompareTo() 而不用對對象進行拆箱操作。 基類描述和或實現相關具體子類的共有行為。接口描述的是不相關的具體類型可以實現的原子功能。都各有千秋。類定義了你創建的類是什么。接口描述實現類的功能行為。一旦你理解這些不同,你就可以創建更高效設計更好面對變化。使用類結構定義相關的類。使用接口暴露實現類的功能。 小結: 這個原則通過一系列的講解告訴接口和類的區別,其實就是說在軟件設計過程中更多關注的是對象能做什么,而不是對象是什么,越多行為的抽象,后期的問題就越少,多使用接口! 歡迎各種不爽,各種噴,寫這個純屬個人愛好,秉持”分享“之德! 有關本書的其他章節翻譯請[點擊查看](/category/297763),轉載請注明出處,尊重原創! 如果您對D.S.Qiu有任何建議或意見可以在文章后面評論,或者發郵件(gd.s.qiu@gmail.com)交流,您的鼓勵和支持是我前進的動力,希望能有更多更好的分享。 轉載請在**文首**注明出處:[http://dsqiu.iteye.com/blog/2083404](/blog/2083404) 更多精彩請關注D.S.Qiu的博客和微博(ID:靜水逐風)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看