🚫 Ad Blocker Detected

Please disable your AD blocker to continue using this site. Ads help us keep the content free! please press keyboard F5 to refresh page after disabled AD blocker

請關閉廣告攔截器以繼續使用本網站。廣告有助於我們保證內容免費。謝謝! 關閉後請按 F5 刷新頁面

0%

樣板模式(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 許可協議。轉載請註明出處!

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