var numbers = new List { 5, 2, 9, 1 };
numbers.Sort(); // 內建支援 IComparable
public class Student : IComparable
{
public int Id { get; set; }
public string Name { get; set; }
public int Score { get; set; }
public int CompareTo(Student other) => this.Score.CompareTo(other.Score); // 固定按分數排
}
缺點:比較邏輯寫死在類別內,無法依情境切換(有時按名字、有時按分數、有時按 ID)。
public class StudentScoreComparer : IComparer
{
public int Compare(Student x, Student y) => x.Score.CompareTo(y.Score);
}
students.Sort(new StudentScoreComparer()); // 靈活指定比較策略
這就是 策略模式 的雛形:將演算法(比較邏輯)封裝成可互換的策略物件。
+----------------+ +---------------------+
| Context |<>------->| Strategy (介面) |
| (ListSorter) | | + Compare(T, T) |
+----------------+ +---------------------+
^ ^
| |
+---------------+ +---------------+
| |
+-------------------+ +-------------------+
| ConcreteStrategyA | | ConcreteStrategyB |
| (ScoreComparer) | | (NameComparer) |
+-------------------+ +-------------------+
public interface IComparer_sly
{
int Compare(T x, T y);
}
public class ListSorter
{
private List _list = new List();
public List Items => _list;
public void Add(T item) => _list.Add(item);
public void AddRange(IEnumerable items) => _list.AddRange(items);
// 核心:接受策略介面,執行排序
public void Sort(IComparer_sly comparer)
{
T[] array = _list.ToArray();
QuickSort(array, 0, array.Length - 1, comparer);
_list = new List(array);
}
private void QuickSort(T[] array, int left, int right, IComparer_sly comparer)
{
if (left >= right) return;
T pivot = array[(left + right) / 2];
int i = left - 1, j = right + 1;
while (true)
{
while (comparer.Compare(array[++i], pivot) < 0) ;
while (comparer.Compare(array[--j], pivot) > 0) ;
if (i >= j) break;
(array[i], array[j]) = (array[j], array[i]); // C# 7+ tuple swap
}
QuickSort(array, left, i - 1, comparer);
QuickSort(array, j + 1, right, comparer);
}
}
public class StudentComparer : IComparer_sly
{
private readonly string _field; // "id" | "name" | "score"
private readonly bool _ascending; // true=升冪, false=降冪
public StudentComparer(string field, bool ascending)
{
_field = field.ToLower();
_ascending = ascending;
}
public int Compare(Student x, Student y)
{
object v1 = GetValue(x), v2 = GetValue(y);
int result = Comparer.Default.Compare(v1, v2); // 處理 null、字串、數值
return _ascending ? result : -result;
}
private object GetValue(Student s) => _field switch
{
"id" => s.Id,
"name" => s.Name,
"score" => s.Score,
_ => null
};
}
var sorter = new ListSorter();
for (int i = 0; i < 10; i++)
sorter.Add(new Student(i, $"Student_{9-i}", 100 - i * 5));
Console.WriteLine("原始順序:");
sorter.Items.ForEach(s => Console.WriteLine($"{s.Id} {s.Name} {s.Score}"));
// 按姓名升冪
sorter.Sort(new StudentComparer("name", true));
Console.WriteLine("\n姓名升冪:");
sorter.Items.ForEach(s => Console.WriteLine($"{s.Id} {s.Name} {s.Score}"));
// 按分數降冪
sorter.Sort(new StudentComparer("score", false));
Console.WriteLine("\n分數降冪:");
sorter.Items.ForEach(s => Console.WriteLine($"{s.Id} {s.Name} {s.Score}"));
反編譯可見:`List
| 優點 | 缺點 |
|---|---|
| 符合開放封閉原則:新增策略不修改既有代碼 | 策略類別數量增加,維護成本略增 |
| 消除條件判斷,程式碼更乾淨 | 客戶端需知曉所有具體策略類別 |
| 演算法可獨立單元測試 | 簡單場景引入策略模式屬於過度設計 |
| 支援執行時熱插拔演算法 |
策略模式將 「什麼時候排序」與「怎麼比較大小」解耦,讓 `ListSorter` 專注於排序演算法本身,而比較規則由外部注入。這正是「針對介面編程、封裝變化」的經典實踐。下次面對多重排序需求時,請優先考慮 `IComparer` + 策略模式,而非在實體類別塞滿 `if (sortBy == "name")`。