iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 25
0

在策略模式使用委派解耦合

我們在先前的文章已用實例來說明GOF的

  • Creational design patterns
  • Structural design patterns
  • Behavioral design patterns

這時候請您再回頭看看六大原則,相信會有更請清楚的領悟!

Principle aka
Single responsibility principle SRP
Liskov Substitution Principle LSP
Dependency inversion principle DIP
Interface Segregation Principle ISP
The Law of Demeter (The least knowledge principle) LoD
The open closed principle OCP

接下來的幾天,我會用幾個案例來說明如何應用設計模式在實際上碰過的需求。
程式碼實作的部分則會以C#跟ASP.NET Core MVC為主;
ASP.NET Core的學習和細節可以參考John Wu大大精彩的系列文章:ASP.NET Core 從入門到實用

在設計程式時,我們通常會不希望業務邏輯相依於實際UI元件、外部資料存取或任何業務邏輯以外的資源。
BUT... 如果我們確實會在邏輯裡面用到這些我們定義和邏輯無關的程式碼怎麼辦?

例如以下的例子...

Scenario

設計上傳檔案並在Server side讀取後轉入資料庫的功能;
但依據上傳檔案種類的不同,在更新資料庫前,讀取已存在資料庫裡面資料的欄位與上傳檔案新的資料做計算後,再更新。

我們會將**用不同檔案,更新資料庫"這一件事用策略模式來設計,因為每一種檔案代表必須執行不同的策略來計算新的欄位值。
但是策略裡面如何讀取和更新資料庫不該是這個策略關心的點,也不應該依賴於資料存取的細節。

所以我們加入委派(delegate or Func or Action)到策略模式中來解耦合。

Why use delegate in Strategy?

我們透過在建立策略實體時,一併提供包裝好的方法給策略裡面的委派,讓策略模式裡面只需要使用這些委派的方法來存取資料,不需要知道細節。

Sample Codes

MVC: View

先建立以下Demo用的View和ViewModel。

Strategy

建立一個策略介面並實作兩個策略類別:

  1. 上傳的託運單檔案已在資料庫中有舊資料時,兩者數量相加為新數量
  2. 上傳的託運單檔案已在資料庫中有舊資料時,以上傳檔案為主覆蓋

並將"查詢資料庫"和"更新資料庫"皆宣告為委派。

  • \Domain\Strategy\
public interface IFoStrategy
{
        Func<int, FreightOrder> Query {get;set;}
        Action<FreightOrder> Update {get; set;}
        void Upload(FreightOrder so);
    
}

///1. 上傳的託運單檔案已在資料庫中有舊資料時,兩者數量相加為新數量
public class FoStrategyAppend : IFoStrategy
{
    public Func<int, FreightOrder> Query { get; set; }
    public Action<FreightOrder> Update { get; set; }
    public void Upload(FreightOrder fo)
    {
        var existFo = this.Query(fo.Id);
        fo.NewAmount = existFo.Amount + fo.Amount;

        //Implement other logic here

        this.Update(fo);
    }
}

///2. 上傳的託運單檔案已在資料庫中有舊資料時,以上傳檔案為主覆蓋
public class FoStrategyReplace: IFoStrategy
{
        public Func<int, FreightOrder> Query {get;set;}
        public Action<FreightOrder> Update {get; set;}
        public void Upload(FreightOrder fo)
        {
            var existFo = this.Query(fo.Id);
            fo.NewAmount = fo.Amount;

            //Implement other logic here

            this.Update(fo);
        }
}

Data access service

在主程式(或DAL層)實作資料存取的方法:

  • \Domain\Strategy\DataAccessService.cs
public class DataAccessService
{
    public static FreightOrder Query(int id)
    {
        Trace.WriteLine("==>查詢資料庫");
        return new FreightOrder{
            Id = 1,
            Customer = "供應商A",
            Product = "塑膠原料",
            Amount = 1000
        };
    }


    public static void Update(FreightOrder fo)
    {
        Trace.WriteLine("==>更新資料庫...");             
    }
}

MVC: Controller

最後我們在Controller裡,依據檔案類型(累加或覆蓋元託運單數項)設定對應策略,並指定策略裡委派的方法。

  • \Controllers\UploadController.cs
[HttpPost]
public IActionResult Index(string fileType, FreightOrder fo)
{
    ModelState.Clear();

    IFoStrategy stg = new FoStrategyAppend(); //採用策略:累加原單之數量
    IFoStrategy stg = new FoStrategyReplace(); //採用策略:覆蓋原單之數量
    stg.Query = DataAccessService.Query;
    stg.Update = DataAccessService.Update;
    updateFreightOrder(fo, stg);

    return View(fo);
}

執行畫面如下:

假設原始已存在資料庫的託運單資料如下:

當我們上傳一份以"累加數量"為策略的檔案時並假設該筆託運單在檔案中的數量為2,000時,所得到最後的新數量為1,000+2,000=3,000。

而當上傳一份以"覆蓋數量"為策略的檔案時並假設該筆託運單在檔案中的數量為2,000時,所得到最後的新數量即為2,000。

Reference


上一篇
別再因為發號碼牌重複被客訴! (Singleton 單例模式)
下一篇
[Design Pattern實例] 在ASP.NET Core利用建造者模式來製造View Model
系列文
Learning Design Pattern in 30 real-case practices30

尚未有邦友留言

立即登入留言