這個做法也是在對 Iterator Methodsyield return 進行延伸討論,並把做法 7 中的 delegate 概念加入進來。

我們已經知道 Iterator Methods 是用來處理集合中資料迭代的功能,但是一個只會進行迴圈的方法是沒什麼意義,通常還會有另一部分 就是對集合中的元素進行處理,例如在做法 31 中把資料去重複就是這樣的概念。

如果我們把這兩種職責寫在同一個方法裡面,那就會產生耦合會導致之後進行修改的時會很難找到問題點,跟 SRP 原則想表達的意思差不多。

也就是說最好是想辦法把這兩種職責分開來,長久來看是比較好的做法,要達到這樣的目的可以使用 做法 7 有提到的 delegate

我們在做法 7 有提到常用的 delegate 格式可以直接用微軟內建的就好,不用自己在建立一個類似的 delegate

namespace System
{
	public delegate bool Predicate<T>(T obj);
	public delegate void Action<T>(T obj);
	public delegate TResult Func<T, TResult>(T arg);
}

例如今天我想要將傳入的集合過濾掉某一個數字,不依靠 delegate 可能會長的像這樣,這個方法同時有迭代跟比較兩種職責存在。

void Main()
{
	var xx = new List<int>() {1, 2, 3, 4, 5, 6,};	
	
	Where<int>(xx, 6).Dump();
}

public static IEnumerable<T> Where<T>(IEnumerable<T> sequence, T filter)
{
	if (sequence == null)
		throw new ArgumentNullException(nameof(sequence), "sequence must not be null");
	foreach (T item in sequence)
		if (!item.Equals(filter))
			yield return item;
}

改成 delegate 的版本會發現比較的職責被移出方法外了,也就是會透過 filterFunc 參數把 delegate 傳入進來,這樣方法就能維持一個職責。

public static IEnumerable<T> Where<T> (IEnumerable<T> sequence, Predicate<T> filterFunc)
{
	if (sequence == null)
		throw new ArgumentNullException(nameof(sequence), "sequence must not be null");
	if (filterFunc == null)
		throw new ArgumentNullException("Predicate must not be null");
	foreach (T item in sequence)
		if (filterFunc(item))
			yield return item;
}

關鍵就是把其它職責透過參數的方式來傳入,也可以改用 Func 產生輸出。

public static IEnumerable<T> Select<T>(IEnumerable<T> sequence, Func<T, T> method)
{
	foreach (T element in sequence)
		yield return method(element);
}

使用起來會像下面這樣,會將我們寫的 Lambda 語法轉換成參數之後進行平方運算完成後輸出結果。

void Main()
{
	var ll = new List<int>() { 1, 2, 3, 4, 5, 6 };
	var r = Select<int>(ll, x => x * x);
	Console.WriteLine(r);
}

也可以使用做法 31 提到 Iterator Methods 的組合特性,將兩個 Iterator Methods 進行結合。

void Main()
{
	var ll = new List<int>() { 1, 2, 3, 4, 5, 6 };
	var r = Select<int>(Where<int>(ll, x => x > 3), x => x * x);
	Console.WriteLine(r);
}

Summary

這個做法在複習做法 7 有提到的內建三個 delegate 方法 Predicate<T>Action<T>Func<> 搭配 Iterator Methods 將不屬於方法的職責移出去來達到解耦的效果, 並且建議把職責都分開來讓每一個方法盡量都只有保持一個職責。