.NET Framework 中的設計模式——應用策略模式為List排序

來源:互聯網
上載者:User
文章目錄
  • 簡單類型排序
  • 編寫通用的List<T>排序方法
  • .NET List的sort如何為我們排序
  • 策略模式
  • 使用情境
  • 策略模式的優勢和不足
  • 參考
簡單類型排序

編程時遇到排序在平常不過,使用.Net最常見的就是對泛型List<T>進行排序,如果T是單一資料型別排序那麼很簡單,直接調用List的Sort()方法就可以了,但是如果我們要排的對象複雜了怎麼辦,我們知道List<T> sort()最後是用快速排序實現,快速排序也好,什麼排序都需要知道list中item之間的比較結果,如果是簡單的int類型,直接判斷即可,對實現了IComparable介面的對象,可以調用其CompareTo()實現item比較大小,下面是一個快速排序的寫法

void Sort<T>(T[] array, int left, int right, IComparer_sly<T> comparer) where T : IComparable        {            if (left < right)            {                T middle = array[(left + right) / 2];                int i = left - 1;                int j = right + 1;                while (true)                {                    while (array[++i].CompareTo(middle) < 0) ;                    while (array[--j].CompareTo(middle) > 0) ;                    if (i >= j)                        break;                    T temp = array[i];                    array[i] = array[j];                    array[j] = temp;                }                Sort(array, left, i - 1, comparer);                Sort(array, j + 1, right, comparer);            }        }

 

 

 

問題

對於前兩種情況固然可以實現排序,但是我們不可能要求所有待排序的對象都實現IComparable介面,就算能夠保證每個對象都實現IComparable介面,如果想實現對象內多個欄位排序,比如Student對象,有時候想按照姓名排序,有時候是成績,有時候是年齡,這怎麼破

按照物件導向的思想,要把變化獨立出來,封裝變化,對於我們排序List<T>時變化的其實就是怎麼比較兩個對象的大小的演算法,如果我們可以把這個演算法拿出來,排序就簡單了很多,無論什麼排序,演算法都是由的,我們要封裝的部分是怎樣比較兩個item的大小的演算法,為了實現拓展性我們要遵循物件導向設計的另外一個重要原則,針對介面編程,而不是針對實現編程。

編寫通用的List<T>排序方法

首先定義一個介面,裡面有一個比較item大小的方法,在排序的時候作為參數傳入,當然是傳入它的實作類別,有了這個想法,我們可以自己寫個List<T>的排序方法

public interface IComparer_sly<T>
{        int Compare(T x, T y);}

 

 

然後為了測試,我們為List<T>加一個封裝,寫一個自己的Sort方法,內部也用快速排序實現。一直困惑我們的變化部分——比較大小演算法,我們把它封轉起來,作為參數傳入

using System;
using System.Collections.Generic;namespace Test.Stategy{public class ListTest<T>    {        public List<T> list = new List<T>();        public void Sort(IComparer_sly<T> comparer)        {            T[] array = list.ToArray();            int left = 0;            int right = array.Length - 1;            QuickSort(array, left, right, comparer);            list = new List<T>(array);        }        private void QuickSort<S>(S[] array, int left, int right, IComparer_sly<S> comparer)        {            if (left < right)            {                S middle = array[(left + right) / 2];                int i = left - 1;                int j = right + 1;                while (true)                {                    while (comparer.Compare(array[++i], middle) < 0) ;                    while (comparer.Compare(array[--j], middle) > 0) ;                    if (i >= j)                        break;                    S temp = array[i];                    array[i] = array[j];                    array[j] = temp;                }                QuickSort(array, left, i - 1, comparer);                QuickSort(array, j + 1, right, comparer);            }        }    }}

比如現在我們有個Student 的實體

public class Student    {        public Student(int id, string name)        {            this.ID = id;            this.Name = name;        }        public int ID { get; set; }        public string Name { get; set; }    }

如果想對這個實體組成的List<T>進行排序,我們只需一個實現 IComparer_sly<Student>的類 StudentComparer,並在內部實現其比較大小方法——Compare(),同時我們可以添加遞增還是遞減排序的控制

class StudentComparer : IComparer_sly<Student>    {        private string expression;        private bool isAscending;        public StudentComparer(string expression, bool isAscending)        {            this.expression = expression;            this.isAscending = isAscending;        }        public int Compare(Student x, Student y)        {            object v1 = GetValue(x), v2 = GetValue(y);            if (v1 is string || v2 is string)            {                string s1 = ((v1 == null) ? "" : v1.ToString().Trim());                string s2 = ((v2 == null) ? "" : v2.ToString().Trim());                if (s1.Length == 0 && s2.Length == 0)                    return 0;                else if (s2.Length == 0)                    return -1;                else if (s1.Length == 0)                    return 1;            }            // 這裡就偷懶調用系統方法,不自己實現了,其實就是比較兩個任意相同類型資料大小,自己實現比較麻煩            if (!isAscending)                return Comparer.Default.Compare(v2, v1);            return Comparer.Default.Compare(v1, v2);        }        private object GetValue(Student stu)        {            object v = null;            switch (expression)            {                case "id":                    v = stu.ID;                    break;                case "name":                    v = stu.Name;                    break;                default:                    v = null;                    break;            }            return v;        }    }

測試一下好不好使

static void Main(string[] args)        {            ListTest<Student> test = new ListTest<Student>();            for (int i = 0; i < 10; i++)            {                Student stu = new Student(i,string.Format("N_"+(9-i)));                test.list.Add(stu);            }            Console.WriteLine("中繼資料");            for (int i = 0; i < test.list.Count;i++ )            {                Console.WriteLine(string.Format("ID:{0} , Name:{1}", test.list[i].ID, test.list[i].Name));            }            Console.WriteLine("Name 遞增");            test.Sort(new StudentComparer("name", true));            for (int i = 0; i < test.list.Count; i++)            {                Console.WriteLine(string.Format("ID:{0} , Name:{1}", test.list[i].ID, test.list[i].Name));            }        }

看看效果

 

 

.NET List的sort如何為我們排序

用ILSpy反編譯可以看到在調用List<T>的sort()方法時內部調用的時 this.Sort(0, this.Count, null); 然後往裡面扒,經過一系列異常處理後會調用 Array.Sort<T>(this._items, index, count, comparer); this._items是把List內容轉換成數組,同樣再經曆一些列異常處理,調用方法 ArraySortHelper<T>.Default.Sort(array, index, length, comparer); 再往裡就和我們上面寫的方法大同小異了,只不過微軟加了很多異常處理和演算法最佳化。

策略模式

看清楚了上面這個例子我們就可以進入正題,說說我們的策略模式了。策略模式定義了一系列的演算法,並將每一個演算法封裝起來,而且使它們還可以相互替換。策略模式讓演算法獨立於使用它的客戶而獨立變化。(原文:The Strategy Pattern defines a family of algorithms,encapsulates each one,and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.)

這個模式涉及到三個角色:

  • 環境(Context)角色:持有一個Strategy類的引用。
  • 抽象策略(Strategy)角色:這是一個抽象角色,通常由一個介面或抽象類別實現。此角色給出所有的具體策略類所需的介面。
  • 具體策略(ConcreteStrategy)角色:封裝了相關的演算法或行為。

相信大家可以分方便的把我們上面例子中的類對應上策略模式的角色,IComparer介面是我們的抽象策略角色, ListTest<T> 類持有抽象策略的引用是環境(在Sort方法中,其實可以把介面定義為類的屬性,在建構函式中賦值,不過不適合此情境,畢竟並不是所有List都需要排序,不能強制其接受一個可能會用不到的介面,當然對每個執行個體都需要用某個策略的情境是合適的),毫無疑問我們實現IComparer抽象策略的類就是具體策略。

使用情境

策略模式很容易理解,不過能夠用它很好的理解鎖裝變化和針對介面編程者兩個物件導向設計原則,我們來看看什麼時候我們會用策略模式

1、 多個類只區別在表現行為不同,可以使用Strategy模式,在運行時動態選擇具體要執行的行為。

2、 需要在不同情況下使用不同的策略(演算法),這些策略有統一介面。

3、 對客戶隱藏具體策略(演算法)的實現細節,彼此完全獨立。

策略模式的優勢和不足

優點:

1、 提供了一種替代繼承的方法,而且既保持了繼承的優點(代碼重用)還比繼承更靈活(演算法獨立,可以任意擴充)。

2、 使用組合,避免程式中使用多重條件轉移語句,使系統更靈活,並易於擴充。

3、 遵守大部分GRASP原則和常用設計原則,高內聚、低偶合。

缺點:

1、 因為每個具體策略類都會產生一個新類,所以會增加系統需要維護的類的數量。

參考

1.http://baike.baidu.com/view/2141079.htm

2.http://www.cnblogs.com/zhenyulu/articles/82017.html

3.Head First設計模式

希望這個系列能夠善始善終,寫了個目錄激勵自己,也方便尋找 .NET Framework 中的設計模式

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.