顯示具有 Design Pattern-筆記 標籤的文章。 顯示所有文章
顯示具有 Design Pattern-筆記 標籤的文章。 顯示所有文章

2021年5月31日 星期一

[Design Pattern] 狀態模式(State Pattern)


前陣子開發電子化表單相關的程式,剛好使用到狀態模式(State Pattern)。

需求是每張表單有其狀態,每個狀態有對應的處理動作,

因此直覺上可以使用狀態模式的概念來處理。


先上個類別圖:



這邊使用抽象類別StateBase,下方的類別就是實際的狀態,

以請假表單來說,會有初始狀態與主管審核後狀態,

Execute方法代表執行狀態對應的動作,可能是狀態移轉或通知對應的人。

而StateContext是管理狀態的類別,其Request方法呼叫state物件的Execute方法,

並注入執行的state instance,以提供狀態執行所需的環境資訊。


剛好這需求,一開始同事是使用switch case來處理各狀態行為,

我改成狀態模式,避免全部的狀態行為都擠在同一個方法,

後續擴充維護也會容易許多,這也是Open–closed principle的精神。

2020年4月18日 星期六

[MVC/Pattern] Unit of Work與Repository模式紀錄

前言:
上一次用到Unif Of Work,已經快3年了,有機會就來記錄一下吧。

先上個圖:


Repository部分讓商業邏輯跟資料存取可以拆開,也是一種pattern,
IRepository定義了常用的CRUD操作,使用泛型處理不同的資料實體,
建立Repository instance時,注入搭配的UnitOfWork instance。

而Unit Of Work負責處理資料交易的部分,確保資料與db是一致的。
當然,多了這些抽象,寫測試也就水到渠成了。

實際程式碼如下:
public interface IUnitOfWork : IDisposable
{ 

	DbContext DbContext { get; set; }
    
	void Commit();

}

public class UnitOfWork : IUnitOfWork
{

        public DbContext DbContext { get; set; }

        public UnitOfWork()
        {
            DbContext = new ApplicationDbContext();
        }

        public void Commit()
        {
            DbContext.SaveChanges();
        }

        public void Dispose()
        {
            DbContext.Dispose();
        }
        
}

public interface IRepository<T> where T : class
{

        IUnitOfWork UnitOfWork { get; set; }

        void Create(T entity);

        IQueryable<T> ReadAll();

        IQueryable<T> Read(Expression<Func<T, bool>> filter);

        void Delete(T entity);

        void Save();

}

public class Repository<T> : IRepository<T> where T : class
{

	private DbSet _entity;
    
	public Repository(IUnitOfWork unitOfWork)
	{
	    UnitOfWork = unitOfWork;
	}

	public IUnitOfWork UnitOfWork { get; set; }

	public void Create(T entity)
        {
            Entity.Add(entity);
        }

        public IQueryable ReadAll()
        {
            return Entity;
        }

        public IQueryable Read(Expression> filter)
        {
            return Entity.Where(filter);
        }

        public void Delete(T entity);
        {
            Entity.Remove(entity);
        }

        public void Save()
        {
            UnitOfWork.Commit();
        }

}


參考資料:
https://docs.microsoft.com/zh-tw/aspnet/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application
https://web.csulb.edu/~pnguyen/cecs475/pdf/repository%20pattern.pdf

2019年12月1日 星期日

[Design Pattern] 策略模式(Strategy Pattern) in C#

前言:
一年前在實作資料處理程式時,不經意的用到該模式,
其實策略模式就是一種Dependency Injection的簡單例子,
因此,實務上很容易使用到。


說明:
類別圖如下:


可以看到Calculator是依賴ICal這個介面,
而實作ICal的類別,就是實作所謂的策略,
例如,將公分數值轉成公尺。
而Calculator可選擇要使用哪個策略,就可得到計算後的結果。


簡單實作如下:








但上面的做法,還可以再改善,將選擇策略的部分移到CalContext,
在建構CalContext時,傳入策略的名稱,就能在GetResult方法內再建立具體策略,
讓外部使用者操作更簡化,如下圖。





2019年8月5日 星期一

[Design Pattern] 範本模式(Template Pattern)

前言:
以前看到範本模式的介紹時,心裡有一個疑問,
看起來就是衍生類別繼承基底類別,為何算一個模式?
直到有一次寫程式不經意用到範本模式,才恍然大悟,
範本不是單純繼承,而是定義好流程,細節留待衍生類別去定義。


說明:
簡單的類別圖如下:


BaseClass就是所謂的範本,定義了Main(),Main()則會執行Start()->Detail()->End(),
其中Start()是private方法,Detail()是abstract方法,讓繼承的類別去實作,
而End()是virtual方法,意即由衍生類別自己決定是否覆寫。


2019年5月24日 星期五

[.Net] 如何複製instance(deep clone)/Prototype Pattern(原型模式)

前言:
有時候需要將物件instance複製出來,
.Net有提供ICloneable介面,以實作複製的方法(實作Clone方法):




而object有提供一擴充方法MemberwiseClone,以執行淺層複製(shallow copy),
但通常要的不是淺層複製,因reference type會指向同一塊heap,
因此若需要完全獨立的instance,就要自己實作。


作法:
1. 自行回傳新建立的instance:
若物件屬性可在建構子就完全決定的話,直接回傳新的instance即可:



不過通常沒這麼簡單,因此可先呼叫MemberwiseClone得到淺層複製,
再自行建立其他reference type屬性的instance:



不過如果物件屬性很多、很複雜又很多層,可考慮下一個做法。

2. 使用Json的Serialize與Deserialize:



不過作法2的執行時間應該比作法1長?? 實際測試一下:


各執行5次,作法2執行時間都在7秒上下,而作法1只要140毫秒,
因此2種作法各有所長,看狀況使用了。


參考資料:
https://docs.microsoft.com/zh-tw/dotnet/api/system.icloneable?view=netframework-4.8
https://docs.microsoft.com/zh-tw/dotnet/api/system.object.memberwiseclone?view=netframework-4.8#System_Object_MemberwiseClone
https://stackoverflow.com/questions/78536/deep-cloning-objects