這個做法是做法 32 的延伸說明,主要在說明把函式當成參數來傳遞所帶來的好處。
首先提出一個沒有使用 Function Parameters 設計的範例,我們在做法 32 有提到使用 Function Parameters 的關鍵是把實做的邏輯由我們
轉交給使用者自行決定這點跟 DIP 原則有點類似,所以如果微軟沒有設計出 Predicate 這幾個 delegate
的概念,那勢必就只能通過介面來
翻轉職責,也就是會變成下面這樣子非常冗長。
void Main()
{
var xx = new List<int>();
var myPredicate = new MyPredicate();
xx.RemoveAll(myPredicate);
}
public interface IPredicate<T>
{
bool Match(T soughtObject);
}
public class List<T>
{
public void RemoveAll(IPredicate<T> match)
{
}
}
public class MyPredicate : IPredicate<int>
{
public bool Match(int target) => target < 100;
}
或者是利用繼承的特性,建立一個抽象的基礎類別之後衍生類別實現需要的抽象方法,這個方法跟上面提到的介面處理耦合都是常用的方式。
下面段程式碼就是個好例子,最內層的 string.Format
就是個具體實現也強烈的跟 Zip
方法耦合在一起。
public static IEnumerable<string> Zip(IEnumerable<string> first, IEnumerable<string> second)
{
using (var firstSequence = first.GetEnumerator())
{
using (var secondSequence = second.GetEnumerator())
{
while (firstSequence.MoveNext() && secondSequence.MoveNext())
{
yield return string.Format("{0} {1}",
firstSequence.Current,
secondSequence.Current);
}
}
}
}
使用 Function Parameters 的設計後,可以把具體的實現移出方法外解除耦合。
public static IEnumerable<TResult> Zip<T1, T2, TResult>(IEnumerable<T1> first, IEnumerable<T2> second, Func<T1, T2, TResult> zipper)
{
using (var firstSequence = first.GetEnumerator())
{
using (var secondSequence = second.GetEnumerator())
{
while (firstSequence.MoveNext() && secondSequence.MoveNext())
{
yield return zipper(firstSequence.Current,secondSequence.Current);
}
}
}
}
最後使用 Zip 方法時需要額外傳入具體 Function 即可。
void Main()
{
var first = "Hello";
var second = "World!";
var result = Zip(first, second, (one, two) => string.Format("{0} {1}", one, two));
}
我們可以把做法 33 使用到的方法進行改寫用 Function Parameters 的設計。
static IEnumerable<int> CreateSequence(int numberOfElements, int startAt, int stepBy)
{
for (var i = 0; i < numberOfElements; i++)
yield return startAt + i * stepBy;
}
public static IEnumerable<T> CreateSequence<T>(int numberOfElements, Func<T> generator)
{
for (var i = 0; i < numberOfElements; i++)
yield return generator();
}
Summary
這個做法跟做法 32 內容差不多,只是多了幾個例子與背後思考的邏輯,使用 Function Parameters 的設計可以解決耦合的問題, 但是我們撰寫程式碼的時候需要花費額外的功夫,當然使用抽象或是介面也是一種好方法,可以按照實際情況搭配使用。