規範模式C#的實現 - Enterprise Craftsmanship
規範模式不是一個新主題,它的許多實現已經在網際網路上。在這篇文章中,我想討論模式的用例,並將幾種常見的實現相互比較。
規格模式:那是什麼?
規範模式是一種模式,它允許我們將一些領域知識封裝到一個單元:規範中 ,並在程式碼庫的不同部分重用這個單元。
該模式的用例最好用一個例子表示。假設我們的域模型中有以下類:
public class Movie : Entity { public string Name { get; } public DateTime ReleaseDate { get; } public MpaaRating MpaaRating { get; } public string Genre { get; } public double Rating { get; } } public enum MpaaRating { [b]G[/b], [b]PG13[/b], [b]R[/b] }
現在,讓我們假設使用者想要找一些相對較新的電影觀看。為了實現這一點,我們可以向儲存庫類新增一個方法,如下所示:
public class MovieRepository { public IReadOnlyList<Movie> GetByReleaseDate(DateTimeminReleaseDate) { /* … */<font> } } </font>
如果我們需要按評級或型別搜尋,我們也可以介紹其他方法:
<b>public</b> <b>class</b> MovieRepository { <b>public</b> IReadOnlyList<Movie> GetByReleaseDate(DateTimemaxReleaseDate) { } <b>public</b> IReadOnlyList<Movie> GetByRating(<b>double</b> minRating) { } <b>public</b> IReadOnlyList<Movie> GetByGenre(string genre) { } }
當我們決定結合搜尋標準時,事情變得有點複雜,但我們仍處於良好狀態。我們可以引入一個Find方法來處理所有可能的標準並返回一個統一的搜尋結果:
<b>public</b> <b>class</b> MovieRepository { <b>public</b> IReadOnlyList<Movie> Find( DateTime? maxReleaseDate = <b>null</b>, <b>double</b> minRating = 0, string genre = <b>null</b>) { <font><i>/* … */</i></font><font> } } </font>
當然,我們也可以隨時為該方法新增其他標準。
當我們不僅需要搜尋資料庫中的資料而且還需要在記憶體中驗證資料時,就會出現問題。例如,在我們向其出售門票之前,我們可能想檢查某部電影是否符合兒童資格,因此我們引入了驗證,如下所示:
<b>public</b> Result BuyChildTicket(<b>int</b> movieId) { Movie movie = _repository.GetById(movieId); <b>if</b> (movie.MpaaRating != MpaaRating.[b]G[/b]) <b>return</b> Error(“The movie is not eligible <b>for</b> children”); <b>return</b> Ok(); }
如果我們還需要檢視滿足相同標準的所有電影的資料庫,我們必須引入類似於以下的方法:
<b>public</b> <b>class</b> MovieRepository { <b>public</b> IReadOnlyList<Movie> FindMoviesForChildren() { <b>return</b> db .Where(x => x.MpaaRating == MpaaRating.[b]G[/b]) .ToList(); } }
此程式碼的問題在於它違反了DRY 原則,因為關於考慮兒童電影的內容的領域知識現在分佈在兩個位置:BuyChildTicket方法和MovieRepository。這就是規範模式可以幫助我們的地方。我們可以介紹一個新的類,它確切地知道如何區分不同型別的電影。然後我們可以在兩種情況下重用此類:
<b>public</b> Result BuyChildTicket(<b>int</b> movieId) { Movie movie = _repository.GetById(movieId); <b>var</b> spec = <b>new</b> MovieForKidsSpecification(); <b>if</b> (!spec.IsSatisfiedBy(movie)) <b>return</b> Error(“The movie is not eligible <b>for</b> children”); <b>return</b> Ok(); } <b>public</b> <b>class</b> MovieRepository { <b>public</b> IReadOnlyList<Movie> Find(Specification<Movie> specification) { <font><i>/* … */</i></font><font> } } </font>
這種方法不僅消除了域知識重複,還允許組合多個規範。反過來,這有助於我們輕鬆設定相當複雜的搜尋和驗證標準。
規範模式有3個主要用例:
- 查詢資料庫中的資料。那就是找到符合我們手頭規格的記錄。
- 驗證記憶體中的物件。換句話說,檢查我們檢索或建立的物件是否符合規範。
- 建立符合條件的新例項。這在您不關心例項的實際內容但仍需要具有某些屬性的情況下非常有用。