樣板模式(TemplatePattern)

前言:

如果目前場景遇到一定流程階段,但流程內容依照邏輯或情境不同也有所不一樣. 這時可以考慮使用樣板模式(TemplatePattern)

生活舉例:

因為十二年國教,所以基本上每個人都有上學的經驗

每天上學最少要經歷下面過程(我做一些簡化)

到學校=>上午上課=>吃午餐=>下午上課=>放學回家

可以看到不管是國小、國中、高中 至少都有上述的過程

但每個過程內容可能會依照年級階段不同,也有所不一樣

例如:

  • 吃中餐:高中可能是吃便當,但國小是吃營養午餐,雖然都是吃飯但內容不一樣。
  • 上午上課:都是教數學,但高中教微積分,國小教加減乘除。
重點:流程雖一樣但細部邏輯交由學校去實施實現

常見例子:

我們常見的測試框架 MSTest,NUnit..... 都有樣板模式的思想。

一般來說測試框架都有生命週期,只是每個框架命名不一樣但核心原理差不多

  1. SetUpClass (每個測試類別只都執行一次)
  2. SetUpUnitTest (每次執行測試方法時都執行一次)
  3. UnitTest (執行測試方法)

如下圖

Alt text

(圖片來自網路上)


程式碼範例:

此範例使用Console來模擬單元測試框架流程:

建立一個 UnitFlowBase 抽像類別依照Nunit生命週期來實現下面方法.

  1. OneTimeSetUp (每個測試類別只都執行一次)
  2. Dispose (每次執行測試方法時都執行一次)
  3. SetUp 每次執行TestCase前資料初始化
  4. TearDown 每次執行TestCase後釋放資源

此抽象類別提供幾個Hock讓子類實做細節。 UnitFlowBase只提供框架

UnitTest對外提供一個void UnitTest(IEnumerable<Func<bool>> testCases)方法.

可以傳入要驗證動作一個IEnumerable<Func<bool>>型別.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public abstract class UnitFlowBase
{
protected virtual void OneTimeSetUp()
{
}

protected virtual void Dispose()
{
}

protected virtual void SetUp()
{
}

protected virtual void TearDown()
{
}

public void UnitTest(IEnumerable<Func<bool>> testCases)
{
OneTimeSetUp();
foreach (var testCase in testCases)
{
SetUp();
Console.WriteLine(testCase() ? "Assert Successful." : "Assert Fail.");
TearDown();
}
Dispose();
}
}

建立另一個類別UnitCounter重載SetUp,OneTimeSetUp方法.

1
2
3
4
5
6
7
8
9
10
11
12
public class UnitCounter : UnitFlowBase
{
protected override void SetUp()
{
Console.WriteLine("Set up UnitCounter thing.");
}

protected override void OneTimeSetUp()
{
Console.WriteLine("OneTimeSetUp!!");
}
}

呼叫實我們建立一個UnitCounter類別,並傳入一個IEnumerable<Func<bool>>的資料集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Program
{
static void Main(string[] args)
{

UnitCounter unitCounter = new UnitCounter();
unitCounter.UnitTest(new List<Func<bool>>()
{
()=>true,
()=>false,
()=>false,
()=>true
});

Console.ReadKey();
}
}

實際案例

在我一個開源專案中ElectronicInvoice_TW,有使用到Template Method Pattern

因為在大平台傳送資料有些固定的流程,這個就很適合使用此Pattern.

  1. 參數需要按照字首排序.
  2. 參數製作簽章防偽造.
  3. 利用Http類別請求大平台.

對於每個API來說有一個變化是傳入參數,所以我就把它當作是此系列類別需要override方法.

而在ApiBase.cs是所有大平台API的Base類別在裡面有一個string ExecuteApi(TModel mode)方法提供給外部呼叫.

詳細資料可自行參閱我的原始碼.


小結:

日後測試程式只需關注我們需要如何實現邏輯細解(重寫三個方法),核心流程順序就交由UnitFlowBase決定。

此文作者:Daniel Shih(石頭)
此文地址https://isdaniel.github.io/template-pattern/
版權聲明:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 3.0 TW 許可協議。轉載請註明出處!


如果本文對您幫助很大,可街口支付斗內鼓勵石頭^^