# [.NET領域驅動設計實戰系列]專題三:前期準備之規約模式(Specification Pattern)
## 一、前言
在專題二中已經應用DDD和SOA的思想簡單構建了一個網上書店的網站,接下來的專題中將會對該網站補充更多的DDD的內容。本專題作為一個準備專題,因為在后面一個專題中將會網上書店中的倉儲實現引入規約模式。本專題將詳細介紹了規約模式。
## 二、什么是規約模式
講到規約模式,自然想到的是什么是規約模式呢?從名字上看,**規約模式就是一個約束條件**,我們在使用倉儲進行查詢的時候,這時候就會牽涉到很多查詢條件,例如名字包含C#的書名等條件。這樣就自然需要引入[規約模式](http://en.wikipedia.org/wiki/Specification_pattern)了。規約模式的作用可以自由組裝業務邏輯元素。Specification類有一個IsSatisifiedBy函數,用于校驗某個對象是否滿足該Specification所表達的條件。多個Specification對象可以組裝起來,生成新的Specification對象,這樣可以通過組裝的方式來定制新的條件。簡單地說,規約模式就是對查詢條件表達式用類的形式進行封裝。那這樣的話,規約模式引入有什么作用呢?
## 三、為什么需要引入規約模式模式
上面只是簡單介紹了規約模式的作用——可以自由組裝業務邏輯元素。這樣文字表述未免枯燥了點,下面通過一個具體例子來說明下。
對于在倉儲中,我們經常會定義下面的接口
接下來就是實現這個接口,并在類中分別實現接口中的方法。這樣設計的好處就是一目了然,可以方便地看到Product倉儲到底提供了哪些功能。
對于這種設計,對于簡單系統并且今后擴展的可能性不大,那么這樣的設計非常合適,因為其簡潔高效。但如果你正在設計一個中大型系統,那么,針對上面的設計,你就需要考慮下面的問題了:
1. 今后如果需要添加新的查詢邏輯,結果一大堆相關代碼都需要修改,上面的設計能便于擴展嗎?
2. 由于業務的擴展,上面的設計會導致接口變得越來越大,團隊成員可能會對這個接口進行修改,添加新的接口方法。
規約模式就是DDD引入解決上面問題的一種模式。下面讓我們來看看規約模式的定義與實現。
## 四、規約模式的傳統實現
首先來看下規約模式的類結構圖:

上圖是摘自維基百科里面的,通過設計圖我們很容易實現規約模式,這樣之所以稱為的傳統實現,因為后面會對該實現應用C#的特性來對該實現進行簡化,使其更加簡單輕量。首先我們需要定義一個ISpecification接口,在接口中定義四個方法:And、Not、Or和IsSatifiedBy方法,具體接口的定義如下所示:
```
// 規約接口的定義
public interface ISpecification<T>
{
bool IsSatisfiedBy(T candidate);
ISpecification<T> And(ISpecification<T> specification);
ISpecification<T> Or(ISpecification<T> specification);
ISpecification<T> Not(ISpecification<T> specification);
}
```
實現了ISpeification的對象意味著是一個Specification,即一種篩選條件,我們可以與其他Specification對象通過And、Or和Not操作來生成新的邏輯,即組合成新的篩選條件,為了方便“組合邏輯”的實現,這里還需要定義一個抽象的CompositeSpecification類:
```
// 因為And,OR和Not方法在所有的Specification都需要實現,只有IsSatisfiedBy方法才依賴業務規則
// 所以為了復用,定義一個抽象類來實現And,Or和And操作,并且留IsSatisfiedBy方法給子類去實現,所以定義其為abstract
public abstract class CompositeSpecification<T>: ISpecification<T>
{
public abstract bool IsSatisfiedBy(T candidate);
public ISpecification<T> And(ISpecification<T> specification)
{
return new AndSpecification<T>(this, specification);
}
public ISpecification<T> Or(ISpecification<T> specification)
{
return new OrSpecification<T>(this, specification);
}
public ISpecification<T> Not(ISpecification<T> specification)
{
return new NotSpecification<T>(specification);
}
}
```
CompositeSpecification提供了構建符合Specification的基礎邏輯,它提供了And、Or和Not方法的實現,讓其他Specification類只需要專注于IsSatisfiedBy方法的實現即可(這里有點模板方法模式的影子)。下面是And、Or和Not規約的具體實現:
```
// AndSpecification,OrSpecification and NotSpecification主要為了組合
**public class AndSpecification<T> : CompositeSpecification<T>**
{
private readonly ISpecification<T> _lefSpecification;
private readonly ISpecification<T> _rightSpecification;
public AndSpecification(ISpecification<T> left, ISpecification<T> right)
{
this._lefSpecification = left;
this._rightSpecification = right;
}
public override bool IsSatisfiedBy(T candidate)
{
return this._lefSpecification.IsSatisfiedBy(candidate)
&& this._rightSpecification.IsSatisfiedBy(candidate);
}
}
**public class OrSpecification<T> : CompositeSpecification<T>**
{
private readonly ISpecification<T> _leftSpecification;
private readonly ISpecification<T> _rightSpecification;
public OrSpecification(ISpecification<T> left, ISpecification<T> right)
{
this._leftSpecification = left;
this._rightSpecification = right;
}
public override bool IsSatisfiedBy(T candidate)
{
return _leftSpecification.IsSatisfiedBy(candidate)
|| _rightSpecification.IsSatisfiedBy(candidate);
}
}
**public class NotSpecification<T> : CompositeSpecification<T>**
{
private readonly ISpecification<T> _specification;
public NotSpecification(ISpecification<T> specification)
{
this._specification = specification;
}
public override bool IsSatisfiedBy(T candidate)
{
return !_specification.IsSatisfiedBy(candidate);
}
}
```
接下來我們可以定義具體的規約模式,如果IdEqualSpecification、NameEqualSpecification規約等。下面就看下引入規約模式后,是如何解決上面倉儲接口設計所存在的問題的。
```
// 引入規約模式,IProductRespository接口的定義
public interface IProductRespository
{
Product GetBySpecification(ISpecification<Product> spec);
IEnumerable<Product> FindBySpecification(ISpecification<Product> spec);
}
public class IdEqualSpecification : CompositeSpecification<Product>
{
private readonly Guid _id;
public IdEqualSpecification(Guid id)
{
_id = id;
}
public override bool IsSatisfiedBy(Product candidate)
{
return candidate.Id.Equals(_id);
}
}
public class NameEqualSpecification : CompositeSpecification<Product>
{
private readonly string _name;
public NameEqualSpecification(string name)
{
_name = name;
}
public override bool IsSatisfiedBy(Product candidate)
{
return candidate.Name.Equals(_name);
}
}
public class NewProductsSpecification : CompositeSpecification<Product>
{
public override bool IsSatisfiedBy(Product candidate)
{
return candidate.IsNew == true;
}
}
```
通過引入規約后,Product倉儲中所有特定用途的操作都刪除了,取而代之的是2個非常簡潔的方法。規約模式解耦了倉儲操作和篩選條件,如果業務擴展,我們可以定制我們的Specification,并將其注入到倉儲即可。倉儲的接口和實現無需任何修改。
下面通過一個具體的演示例子來看下傳統規約模式的應用。具體的場景是這樣的:我們想篩選一批int數組中的偶數和大于0的數字出來。因為這里涉及2個篩選條件,一個是偶數,一個是大于0的數,這樣我們就可以通過定義偶數規約和正數規約。具體的實現如下所示:
```
// 具體規約,偶數規約
public class EvenSpecification : CompositeSpecification<int>
{
public override bool IsSatisfiedBy(int candidate)
{
return candidate % 2 == 0;
}
}
// 具體的規約,正數規約
public class PlusSpecification : CompositeSpecification<int>
{
public override bool IsSatisfiedBy(int candidate)
{
return candidate > 0;
}
}
```
接下來通過And操作和將2中規約組合起來形成新的規約。具體的測試代碼如下所示:
```
using spec1 =SpecificationPatternDemo.Specification;
class Program
{
static void Main(string[] args)
{
Demo1();
Console.Read();
}
public static void Demo1()
{
var items = Enumerable.Range(-5, 10);
Console.WriteLine("產生的數組為:{0}", string.Join(", ", items.ToArray()));
spec1.ISpecification<int> evenSpec = new spec1.EvenSpecification();
// 獲得一個組合規約
var compositeSpecification = GetCompositeSpecification(evenSpec);
// 類似Where(it=>it%2==0 && it > 0)
// 前者是把兩個條件合并寫死成一個條件,而后者是將其組合成一個新條件。就如拼圖出一架飛機和直接制造一個飛機模型概念是完全不同的
foreach (var item in items.Where(it=>compositeSpecification.IsSatisfiedBy(it)))
{
// 輸出既是正數又是偶數的數
Console.WriteLine(item);
}
}
private static spec1.ISpecification<int> GetCompositeSpecification(spec1.ISpecification<int> spec)
{
spec1.ISpecification<int> plusSpec = new spec1.PlusSpecification();
return spec.And(plusSpec);
}
}
```
具體的運行結果如下圖所示:

上面我們已經介紹完規約模式的實現,并且通過對比的方式來介紹引入規約模式所解決之前的問題。但是傳統規約模式的實現顯得非常臃腫。因為你想實現一個新的規約,你需要新增一個新的Specification類,這樣下來,我們的項目中必然會堆積大量的Specification類。有些規約可能只只使用了一次。這就好比.NET里的委托方法一樣,為了解決類似的問題,.NET引入了匿名方法和lamada表達式。同樣,我們借助C#的特性也可以使得傳統規約模式的實現更輕量。下面就具體看下規約模式的輕量實現是如何去實現的。
## 五、規約模式的輕量實現
從上面可以看出,規約模式的關鍵在于IsSatisifiedBy函數,該函數用于校驗某個對象是否滿足該規約所表示的條件,IsSatisifiedBy函數的返回類型為bool類型,這樣我們完全可以讓一個ISpecification只具有IsSatisifiedBy函數。然后該函數返回一個委托調用結果。至于原本ISpecification中的And、Or和Not方法,我們將它們提起成擴展方法。經過上面的分析,輕量化后的規約模式實現也就出來了。具體實現如下所示:
```
public interface ISpecification<in T>
{
bool IsSatisfiedBy(T candidate);
}
public class Specification<T> : ISpecification<T>
{
private readonly Func<T, bool> _isSatisfiedBy;
public Specification(Func<T, bool> isSatisfiedBy)
{
this._isSatisfiedBy = isSatisfiedBy;
}
public bool IsSatisfiedBy(T candidate)
{
return _isSatisfiedBy(candidate);
}
}
public static class SpecificationExtensions
{
public static ISpecification<T> And<T>(this ISpecification<T> left, ISpecification<T> right)
{
return new Specification<T>(candidate => left.IsSatisfiedBy(candidate) && right.IsSatisfiedBy(candidate));
}
public static ISpecification<T> Or<T>(this ISpecification<T> left, ISpecification<T> right)
{
return new Specification<T>(candidate => left.IsSatisfiedBy(candidate) || right.IsSatisfiedBy(candidate));
}
public static ISpecification<T> Not<T>(this ISpecification<T> one)
{
return new Specification<T>(candidate => !one.IsSatisfiedBy(candidate));
}
}
```
使用擴展方法的好處在于,如果我們要加一個邏輯運行,如異或,那么就不需要修改接口了。修改接口是一個不推薦的的事情。因為接口修改會破壞之前已經發布的接口實現。因此,一旦接口發布之后,它就不能被修改了。這意味著,我們在定義接口時應該仔細推敲,做到接口的職責應該尤其單一。
輕量的實現使得使用Specification對象容易多了,我們不需要為每段邏輯創建一個獨立的Specification類。下面具體看下規約模式的輕量實現的使用示例:
```
using SpecificationPatternDemo.Specification_2;
using spec2 = SpecificationPatternDemo.Specification_2;
class Program
{
static void Main(string[] args)
{
Demo2();
Console.Read();
}
public static void Demo2()
{
var items = Enumerable.Range(-5, 10);
Console.WriteLine("產生的數組為:{0}", string.Join(", ", items.ToArray()));
spec2.ISpecification<int> evenSpec = new spec2.Specification<int>(it => it % 2 == 0);
var compositeSpec = GetCompositeSpecification2(evenSpec);
foreach (var i in items.Where(it => compositeSpec.IsSatisfiedBy(it)))
{
Console.WriteLine(i);
}
}
private static spec2.ISpecification<int> GetCompositeSpecification2(spec2.ISpecification<int> spec)
{
spec2.ISpecification<int> plusSpec = new spec2.Specification<int>(it => it > 0);
return spec.And(plusSpec);
}
}
```
從上面的例子可以看出,此時并不需要定義單獨的Specification對象了,只需要用委托來代替即可。其運行結果與上面傳統實現一樣。其實,還可以更簡單,我們可以直接使用一個委托,而不不需要定義ISpecification接口和其Specification實現。其實現方式如下所示:
```
// 更輕量的實現
public static class SpecExtensitions
{
public static Func<T, bool> And<T>(this Func<T, bool> left, Func<T, bool> right)
{
return candidate => left(candidate) && right(candidate);
}
public static Func<T, bool> Or<T>(this Func<T, bool> left, Func<T, bool> right)
{
return candidate => left(candidate) || right(candidate);
}
public static Func<T, bool> Not<T>(this Func<T, bool> one)
{
return candidate => !one(candidate);
}
}
```
上面的實現,我們就只需要一個擴展方式就可以了,其使用示例代碼如下所示:
```
class Program
{
static void Main(string[] args)
{
Demo3();
Console.Read();
}
public static void Demo3()
{
var items = Enumerable.Range(-5, 10);
Console.WriteLine("產生的數組為:{0}", string.Join(", ", items.ToArray()));
Func<int, bool> evenSpec = it => it % 2 == 0;
var compositeSpec = GetCompositeSpec(evenSpec);
foreach (var i in items.Where(it => compositeSpec(it)))
{
Console.WriteLine(i);
}
}
private static Func<int, bool> GetCompositeSpec(Func<int, bool> spec)
{
return spec.And(it => it > 0);
}
}
```
## 六、規約模式的輕量實現的完善——對Linq查詢支持
上面輕量級的Specification模式拋棄了具體的Specification類型,而是使用一個委托對象關鍵的IsSatisfiedBy方法。其優勢在于使用簡單,但是該實現不能支持Linq查詢或表達式的場景。因為EF中的DbContext.Dbset集合的進行where篩選參數只能是表達式樹。所以我們不能用委托對象來判斷邏輯,取而代之的使用表達式樹。對于表達式樹的構造主要由參數和主體構造,所以針對于Not方法可以如下的方式來實現:
```
public static class SpecExprExtensions
{
public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> one)
{
var candidateExpr = one.Parameters[0];
var body = Expression.Not(one.Body);
return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
}
}
```
對于Not方法,我們只要獲取它的參數表達式,再將它的Body外包一個Not表達式,便可以此構造一個新的表達式了。但And和Or方法實現不能像Not一樣簡單處理:
```
// 不能這么處理
public static Expression<Func<T, bool>> And<T>(
this Expression<Func<T, bool>> one, Expression<Func<T, bool>> another)
{
var candidateExpr = one.Parameters[0];
var body = Expression.And(one.Body, another.Body);
return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
}
```
因為one和another兩個表達式雖然都是同樣的形式(Expression<Func<T, bool>>),但是它們的“參數”不是同一個對象。即one.Body和another.Body并沒有公用一個ParameterExpression實例,于是無論采用哪個表達式的參數,在Expression.Lambda方法調用的時候,都會出現body中的某個參數對象并沒有出現在參數列表中的錯誤。
既然參數不一致,所以要實現And和Or方法,必須統一兩個表達式樹的參數。為了達到這個目標,我們可以利用[ExpressionVisitor](https://msdn.microsoft.com/zh-cn/library/system.linq.expressions.expressionvisitor(v=vs.110).aspx)類來實現。這里定義一個派生于[ExpressionVisitor](https://msdn.microsoft.com/zh-cn/library/system.linq.expressions.expressionvisitor(v=vs.110).aspx)的類。具體實現如下:
```
internal class ParameterReplacer : ExpressionVisitor
{
public ParameterReplacer(ParameterExpression paramExpr)
{
this.ParameterExpression = paramExpr;
}
public ParameterExpression ParameterExpression { get; private set; }
public Expression Replace(Expression expr)
{
return this.Visit(expr);
}
protected override Expression VisitParameter(ParameterExpression p)
{
return this.ParameterExpression;
}
}
```
Expressionvistor可以用于求值、變形等各種操作。它提供了遍歷表達式樹的標準方式,如果你直接繼承這個類并調用Visit方法(如上面Replace方法的實現一樣),那么最終返回的結果便是傳入的Expresssion參數本身。但是,如果你覆蓋任意一個方法,返回了與傳入時不同的對象,那么最終的結果就是一個新的Expression對象。就如上面VisitParameter方法實現一樣。它直接返回我們定義的ParameterExpression對象。
通過上面分析,ParameterExpression類的作用是將一個表達式里的所有ParameterExpression替換成我們指定的新對象,這樣就可以解決之前參數不一致的情況。所以我們And和Or方法的實現就是將兩個表達式樹參數替換成我們首先定義好的參數表達式。具體的實現方式如下所示:
```
public static class SpecExprExtensions
{
public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> one)
{
var candidateExpr = one.Parameters[0];
var body = Expression.Not(one.Body);
return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> one,
Expression<Func<T, bool>> another)
{
// 首先定義好一個ParameterExpression
var candidateExpr = Expression.Parameter(typeof (T), "candidate");
var parameterReplacer = new ParameterReplacer(candidateExpr);
// 將表達式樹的參數統一替換成我們定義好的candidateExpr
var left = parameterReplacer.Replace(one.Body);
var right = parameterReplacer.Replace(another.Body);
var body = Expression.And(left, right);
return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
}
public static Expression<Func<T, bool>> Or<T>(
this Expression<Func<T, bool>> one, Expression<Func<T, bool>> another)
{
var candidateExpr = Expression.Parameter(typeof (T), "candidate");
var parameterReplacer = new ParameterReplacer(candidateExpr);
var left = parameterReplacer.Replace(one.Body);
var right = parameterReplacer.Replace(another.Body);
var body = Expression.Or(left, right);
return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
}
}
```
到此,我們就完成了規約模式對Linq支持的輕量實現了。下面讓我們看看上面輕量實現是如何調用的呢?具體調用代碼如下:
```
class Program
{
private static void Main(string[] args)
{
Demo1();
Console.Read();
}
public static void Demo1()
{
var items = Enumerable.Range(-5, 10);
Console.WriteLine("產生的數組為:{0}", string.Join(", ", items.ToArray()));
Expression<Func<int, bool>> f = i => i % 2 != 0;
f = f.Not().And(i => i > 0);
// 通過AsQueryable成IQueryable<int>,因為IQueryable<T>的Where方法的參數要求是表達式樹
foreach (var i in items.AsQueryable().Where(f))
{
Console.WriteLine(i);
}
}
}
```
其運行結果與前面的例子中的運行結果一樣,一樣成功返回了即是偶數又是正數的集合。
## 七、總結
到這里,規約模式的實現就結束了,后期將會在網上書店的案例中引入規約模式,dax.net的Byteart Retail案例中規約模式的實現即包括了傳統實現,也包括了對Linq支持的輕量實現。開始我認為傳統實現是多余的,因為你已經有了規約模式的輕量實現了,何必又有傳統實現呢?這不是包括兩種實現嗎?后面仔細想想,這樣設計也有其存在的道理,因為對于一些邏輯復雜的規約實現,我們可以新建一個具體的規約類,但對于一些簡單和僅使用一次的規約邏輯,就可以直接用表達式樹來代替,就不需要單獨為該段邏輯單獨新建一個具體的規約類。這樣的實現就如同,有了匿名方法和Lambda表達式,是不是委托就可以不需要了。顯然不是的,所以我在我的網上書店案例中也將會引入這兩種實現,讓用戶可以靈活選擇這兩種方式。在下一專題,我繼續介紹一個前期準備的內容,即工作單元模式(Unit Of Work,即UOW)。
本專題的所有源碼下載:[SpecificationPatternDemo.zip](http://files.cnblogs.com/files/zhili/SpecificationPatternDemo.zip)
- C# 基礎知識系列
- C# 基礎知識系列 專題一:深入解析委托——C#中為什么要引入委托
- C# 基礎知識系列 專題二:委托的本質論
- C# 基礎知識系列 專題三:如何用委托包裝多個方法——委托鏈
- C# 基礎知識系列 專題四:事件揭秘
- C# 基礎知識系列 專題五:當點擊按鈕時觸發Click事件背后發生的事情
- C# 基礎知識系列 專題六:泛型基礎篇——為什么引入泛型
- C# 基礎知識系列 專題七: 泛型深入理解(一)
- C# 基礎知識系列 專題八: 深入理解泛型(二)
- C# 基礎知識系列 專題九: 深入理解泛型可變性
- C#基礎知識系列 專題十:全面解析可空類型
- C# 基礎知識系列 專題十一:匿名方法解析
- C#基礎知識系列 專題十二:迭代器
- C#基礎知識 專題十三:全面解析對象集合初始化器、匿名類型和隱式類型
- C# 基礎知識系列 專題十四:深入理解Lambda表達式
- C# 基礎知識系列 專題十五:全面解析擴展方法
- C# 基礎知識系列 專題十六:Linq介紹
- C#基礎知識系列 專題十七:深入理解動態類型
- 你必須知道的異步編程 C# 5.0 新特性——Async和Await使異步編程更簡單
- 全面解析C#中參數傳遞
- C#基礎知識系列 全面解析C#中靜態與非靜態
- C# 基礎知識系列 C#中易混淆的知識點
- C#進階系列
- C#進階系列 專題一:深入解析深拷貝和淺拷貝
- C#進階系列 專題二:你知道Dictionary查找速度為什么快嗎?
- C# 開發技巧系列
- C# 開發技巧系列 使用C#操作Word和Excel程序
- C# 開發技巧系列 使用C#操作幻燈片
- C# 開發技巧系列 如何動態設置屏幕分辨率
- C# 開發技巧系列 C#如何實現圖片查看器
- C# 開發技巧 如何防止程序多次運行
- C# 開發技巧 實現屬于自己的截圖工具
- C# 開發技巧 如何使不符合要求的元素等于離它最近的一個元素
- C# 線程處理系列
- C# 線程處理系列 專題一:線程基礎
- C# 線程處理系列 專題二:線程池中的工作者線程
- C# 線程處理系列 專題三:線程池中的I/O線程
- C# 線程處理系列 專題四:線程同步
- C# 線程處理系列 專題五:線程同步——事件構造
- C# 線程處理系列 專題六:線程同步——信號量和互斥體
- C# 多線程處理系列專題七——對多線程的補充
- C#網絡編程系列
- C# 網絡編程系列 專題一:網絡協議簡介
- C# 網絡編程系列 專題二:HTTP協議詳解
- C# 網絡編程系列 專題三:自定義Web服務器
- C# 網絡編程系列 專題四:自定義Web瀏覽器
- C# 網絡編程系列 專題五:TCP編程
- C# 網絡編程系列 專題六:UDP編程
- C# 網絡編程系列 專題七:UDP編程補充——UDP廣播程序的實現
- C# 網絡編程系列 專題八:P2P編程
- C# 網絡編程系列 專題九:實現類似QQ的即時通信程序
- C# 網絡編程系列 專題十:實現簡單的郵件收發器
- C# 網絡編程系列 專題十一:實現一個基于FTP協議的程序——文件上傳下載器
- C# 網絡編程系列 專題十二:實現一個簡單的FTP服務器
- C# 互操作性入門系列
- C# 互操作性入門系列(一):C#中互操作性介紹
- C# 互操作性入門系列(二):使用平臺調用調用Win32 函數
- C# 互操作性入門系列(三):平臺調用中的數據封送處理
- C# 互操作性入門系列(四):在C# 中調用COM組件
- CLR
- 談談: String 和StringBuilder區別和選擇
- 談談:程序集加載和反射
- 利用反射獲得委托和事件以及創建委托實例和添加事件處理程序
- 談談:.Net中的序列化和反序列化
- C#設計模式
- UML類圖符號 各種關系說明以及舉例
- C#設計模式(1)——單例模式
- C#設計模式(2)——簡單工廠模式
- C#設計模式(3)——工廠方法模式
- C#設計模式(4)——抽象工廠模式
- C#設計模式(5)——建造者模式(Builder Pattern)
- C#設計模式(6)——原型模式(Prototype Pattern)
- C#設計模式(7)——適配器模式(Adapter Pattern)
- C#設計模式(8)——橋接模式(Bridge Pattern)
- C#設計模式(9)——裝飾者模式(Decorator Pattern)
- C#設計模式(10)——組合模式(Composite Pattern)
- C#設計模式(11)——外觀模式(Facade Pattern)
- C#設計模式(12)——享元模式(Flyweight Pattern)
- C#設計模式(13)——代理模式(Proxy Pattern)
- C#設計模式(14)——模板方法模式(Template Method)
- C#設計模式(15)——命令模式(Command Pattern)
- C#設計模式(16)——迭代器模式(Iterator Pattern)
- C#設計模式(17)——觀察者模式(Observer Pattern)
- C#設計模式(18)——中介者模式(Mediator Pattern)
- C#設計模式(19)——狀態者模式(State Pattern)
- C#設計模式(20)——策略者模式(Stragety Pattern)
- C#設計模式(21)——責任鏈模式
- C#設計模式(22)——訪問者模式(Vistor Pattern)
- C#設計模式(23)——備忘錄模式(Memento Pattern)
- C#設計模式總結
- WPF快速入門系列
- WPF快速入門系列(1)——WPF布局概覽
- WPF快速入門系列(2)——深入解析依賴屬性
- WPF快速入門系列(3)——深入解析WPF事件機制
- WPF快速入門系列(4)——深入解析WPF綁定
- WPF快速入門系列(5)——深入解析WPF命令
- WPF快速入門系列(6)——WPF資源和樣式
- WPF快速入門系列(7)——深入解析WPF模板
- WPF快速入門系列(8)——MVVM快速入門
- WPF快速入門系列(9)——WPF任務管理工具實現
- ASP.NET 開發
- ASP.NET 開發必備知識點(1):如何讓Asp.net網站運行在自定義的Web服務器上
- ASP.NET 開發必備知識點(2):那些年追過的ASP.NET權限管理
- ASP.NET中實現回調
- 跟我一起學WCF
- 跟我一起學WCF(1)——MSMQ消息隊列
- 跟我一起學WCF(2)——利用.NET Remoting技術開發分布式應用
- 跟我一起學WCF(3)——利用Web Services開發分布式應用
- 跟我一起學WCF(3)——利用Web Services開發分布式應用
- 跟我一起學WCF(4)——第一個WCF程序
- 跟我一起學WCF(5)——深入解析服務契約 上篇
- 跟我一起學WCF(6)——深入解析服務契約 下篇
- 跟我一起學WCF(7)——WCF數據契約與序列化詳解
- 跟我一起學WCF(8)——WCF中Session、實例管理詳解
- 跟我一起學WCF(9)——WCF回調操作的實現
- 跟我一起學WCF(10)——WCF中事務處理
- 跟我一起學WCF(11)——WCF中隊列服務詳解
- 跟我一起學WCF(12)——WCF中Rest服務入門
- 跟我一起學WCF(13)——WCF系列總結
- .NET領域驅動設計實戰系列
- .NET領域驅動設計實戰系列 專題一:前期準備之EF CodeFirst
- .NET領域驅動設計實戰系列 專題二:結合領域驅動設計的面向服務架構來搭建網上書店
- .NET領域驅動設計實戰系列 專題三:前期準備之規約模式(Specification Pattern)
- .NET領域驅動設計實戰系列 專題四:前期準備之工作單元模式(Unit Of Work)
- .NET領域驅動設計實戰系列 專題五:網上書店規約模式、工作單元模式的引入以及購物車的實現
- .NET領域驅動設計實戰系列 專題六:DDD實踐案例:網上書店訂單功能的實現
- .NET領域驅動設計實戰系列 專題七:DDD實踐案例:引入事件驅動與中間件機制來實現后臺管理功能
- .NET領域驅動設計實戰系列 專題八:DDD案例:網上書店分布式消息隊列和分布式緩存的實現
- .NET領域驅動設計實戰系列 專題九:DDD案例:網上書店AOP和站點地圖的實現
- .NET領域驅動設計實戰系列 專題十:DDD擴展內容:全面剖析CQRS模式實現
- .NET領域驅動設計實戰系列 專題十一:.NET 領域驅動設計實戰系列總結