閱讀目錄:
- 1.開篇介紹
- 2.程式書籤(程式碼書籤機制)
- 2.1ProgramBookmark 實現(使用委託來錨點程式碼書籤)
- 2.2ProgramBookmarkManager書籤管理器(物件化書籤集合的處理,IEnumerable<T>書籤管理)
- 3.可恢復語句元件(將語句物件化)
- 3.1可恢復語句元件管理器(將可恢復語句視為普通的物件成員,IEnumerable<T>可恢復語句元件)
- 3.2可恢復語句元件執行時(Program CLR(簡介))
- 3.3可恢復語句邏輯配置(規則的配置(簡介))
- 3.4可恢復語句邏輯傳輸(將邏輯語句物件遠端傳輸(簡介))
- 4.DomainModel規則引擎(規則持久化後管理配置(簡介))
1】開篇介紹
這一篇文章我早準備寫的,遲遲未寫的原因是它過於抽象不太容易表達,也很難掌握;之前對它的理解還處於比較簡單的功能性上,但是最近隨著對領域驅動設計及架構的研究,設計思想有了一個提升對它的理解也有了一個更清晰的輪廓,所以才敢下手去寫,這麼好的一篇文章不能搞砸了;
“鈍化語句” 簡單描述:將基於棧的呼叫抽象成基於我們自己構建的虛擬執行時呼叫;
比如我們可以將普通的IF\ELSE呼叫進行物件化,然後就可以對他們進行物件導向的設計了;能做的事情就太多了,比如將所有的方法放入一個for迴圈語句元件當中去,它會自動的去迴圈執行,而不需要我們再去自己寫for語句;然後在此基礎上進行程式碼書籤抽象對所有的程式碼片段進行類似邏輯錨點的設定;
更嚇人的是可以瞬間將語句元件鈍化,其實也就是瞬間凍結然後持久化,在遙遠的地方再將它喚醒執行,很可能你的語句在你這臺電腦上執行了一半由於你臨時有事然後語句被鈍化,在另外一臺電腦上繼續你的工作,是不是很方便;當然它的使用方式多種多樣了;
我相信這篇文章絕對讓你對 .NET框架設計 感興趣,框架設計思想其實真的很美,讓人陶醉;
2】程式書籤(程式碼書籤機制)
美好的一切都要有一個良性的開始,程式的鈍化少不了對程式的邏輯儲存的功能;有一個連續的呼叫穿過N個方法,方法一呼叫方法二,方法二呼叫方法三,這樣的呼叫層次是根據業務的需求來定的,就好比一個複雜的業務邏輯這樣的處理下去合情合理;
那麼什麼是程式碼書籤呢?其實我們仔細分析一下我們日常所寫的程式碼基本上都是由方法組合而成,不管是例項類還是靜態類都是通過方法將彼此聯絡起來,所有的業務邏輯都是包裝在方法的內部處理的,這裡的程式碼書籤就是方法的可持久化抽象;
試想一下,我們要想將程式的邏輯流程鈍化肯定是少不了對邏輯呼叫的儲存;原本的程式邏輯是執行緒本地的執行路徑,屬於.NETCLR直接管理的,依賴於棧的執行,所以我們無法干預其生命週期過程,那麼我們只有將它們物件化後才能由我們自己操控;
圖1:
上圖的意思是說在一個流程的開始到結束基本上三個重要環節,Begin\Processs…\End過程,在每個過程中需要不同的處理邏輯,在圖的偏上方,我們有三個ProcessName名稱的小方塊表示程式的呼叫順序,ProcessName1呼叫ProcessName2呼叫ProcessName3;
在ProcessName2的上面我們加了一個Bookmark的標記,表示我們這裡所說的程式碼書籤,通過程式碼書籤我們就可以記錄下本次執行到哪裡了,就好比我們在看書的時候都有一個買書時贈送的書籤卡,我們看到哪裡就把這個書籤卡插在那裡,當下次要看的時候直接找到這個書籤卡繼續看;
這裡的程式碼書籤跟這個是一樣的道理,理論就是這些我們下面通過示例程式碼來親身體驗一下這種設計模式;
2.1】ProgramBookmark 實現(使用委託來錨定程式碼書籤)
委託是天生的方法標籤,通過委託我們完全可以將一個例項的方法直接錨定下來;
【有關對委託的高階應用不太清楚的可以參見本人的這兩篇文章:
我們來構造程式碼書籤物件:
1 /*============================================================================== 2 * Author:深度訓練 3 * Create time: 2013-08-10 4 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ 5 * Author Description:特定領域軟體工程實踐; 6 *==============================================================================*/ 7 8 namespace ProgramComponent 9 { 10 using System; 11 12 /// <summary> 13 /// Program book mark. 14 /// </summary> 15 [Serializable] 16 public class ProgramBookmark 17 { 18 /// <summary> 19 /// Mark program book mark. 20 /// </summary> 21 /// <param name="name">Mark name.</param> 22 /// <param name="continueAt">Program continue.</param> 23 public ProgramBookmark(string name, ProgramBookmarkLocation continueAt) 24 { 25 this.markname = name; 26 this.continueAt = continueAt; 27 } 28 29 private string markname; 30 /// <summary> 31 /// Book mark name. 32 /// </summary> 33 public string BookmarkName { get { return markname; } } 34 35 private ProgramBookmarkLocation continueAt; 36 /// <summary> 37 /// Continue location. 38 /// </summary> 39 public ProgramBookmarkLocation ContinueAt { get { return continueAt; } } 40 41 /// <summary> 42 /// Program load data. 43 /// </summary> 44 public object Payload { get; set; } 45 } 46 /// <summary> 47 /// Program book mark location. 48 /// </summary> 49 /// <param name="resumed">Resumed bookmark.</param> 50 public delegate void ProgramBookmarkLocation(ProgramBookmark resumed); 51 }
這段程式碼是對程式碼書籤的抽象,建構函式傳入一個程式碼書籤的名稱、書籤所表示的物理程式碼錨點,Payload是表示每次執行物理程式碼時的輸入引數;
上面程式碼看似簡單其實很不簡單,它的背後隱藏著一個很大的設計思想:
將一塊很大的邏輯程式碼拆成很多零碎的方法片段,很多人可能會覺得設計本身不就這樣要求的嘛,那你可能真的沒有深入理解程式碼碎片會後需要對所有的方法引數進行物件化,不管什麼方法都會是同樣的引數,只有這樣才能讓書籤連續起作用;
下面我們來看一下程式碼書籤有多巧妙,我們來構造一個簡單的示例程式碼,當然你完全可以設計的很複雜很強大,這裡畢竟是傳遞這種設計思想為主;
1 /*============================================================================== 2 * Author:深度訓練 3 * Create time: 2013-08-10 4 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ 5 * Author Description:特定領域軟體工程實踐; 6 *==============================================================================*/ 7 8 using System; 9 using System.Collections.Generic; 10 using System.Linq; 11 using System.Text; 12 using System.Threading.Tasks; 13 14 namespace ConsoleApplication1 15 { 16 [Serializable] 17 public class OrderCheckFlows 18 { 19 private IList<ProgramComponent.ProgramBookmark> flowsManager = new List<ProgramComponent.ProgramBookmark>(); 20 21 public OrderCheckFlows() 22 { 23 ProgramComponent.ProgramBookmark bookmarkCheckOrderPrices = 24 new ProgramComponent.ProgramBookmark("checkPrices", new ProgramComponent.ProgramBookmarkLocation(CheckOrderPrices)); 25 26 flowsManager.Add(bookmarkCheckOrderPrices); 27 } 28 29 public void StartCheck() 30 { 31 do 32 { 33 flowsManager[0].ContinueAt(flowsManager[0]); 34 } 35 while (flowsManager.Count > 0); 36 } 37 38 #region business flows 39 public void CheckOrderPrices(ProgramComponent.ProgramBookmark nextCheck) 40 { 41 Console.WriteLine("checkPrices..."); 42 43 ProgramComponent.ProgramBookmark bookmarkCheckOrderPrices = 44 new ProgramComponent.ProgramBookmark("checkPrices", new ProgramComponent.ProgramBookmarkLocation(CheckOrderItems)); 45 bookmarkCheckOrderPrices.Payload = true;//method parameters. 46 flowsManager.Add(bookmarkCheckOrderPrices); 47 48 flowsManager.RemoveAt(0); 49 } 50 51 public void CheckOrderItems(ProgramComponent.ProgramBookmark nextCheck) 52 { 53 if ((bool)nextCheck.Payload) 54 { 55 Console.WriteLine("checkItems..."); 56 } 57 else 58 { 59 Console.WriteLine("end check items."); 60 } 61 flowsManager.RemoveAt(0); 62 } 63 #endregion 64 } 65 }
這個類是一個簡單的模擬檢查訂單的一系列的業務流程;
圖2:
上圖能看見流程順利執行完畢了,那麼我們來解釋一下重要的程式碼片段;
圖3:
在第一個流程裡面我們構造一個通往下一個流程的 ProgramComponent.ProgramBookmark 物件,如果這裡出現關於流程無法繼續下去的條件就可以不建立往下執行的程式碼書籤;在第二流程裡面我們獲取第一個流程設定的引數,這裡是一個Bool值,可以用來判斷上一個執行是否成功等資訊;
2.2】ProgramBookmarkManager書籤管理器(書籤集合的處理,IEnumerable<T>書籤管理)
上一節我們完成了對程式碼書籤的抽象實現,但是程式碼還有很多值得抽象設計的地方,上面的程式碼中最不太理解的地方就是對書籤集合的操作上,很不OO;
那麼這一節我們將把它改進,形成OO方式的呼叫,先看一下哪裡不太理解;
圖4:
第一個地方就是在宣告ProgramCompoent.ProgramBookmark集合上,這樣寫問題太大了,無法進行擴充套件改進;然後就是在建構函式中,我們使用了很長一段程式碼來構造一個ProgramCompoent.ProgramBookmark物件,完全可以減少很多;還有就是在StartCheck方法的內部中進行迴圈呼叫書籤的程式碼,也很有問題,完全可以封裝在內部實現,外部直接一個CurrentProgram屬性執行就行了;
那麼對這些問題我們其實少一個ProgramCompoent.ProgramBookmark的管理器物件ProgramCompoent.ProgramBookmarkManager物件,它負責管理所有跟ProgramCompoent.ProgramBookmark物件相關的工作;
1 /*============================================================================== 2 * Author:深度訓練 3 * Create time: 2013-08-10 4 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ 5 * Author Description:特定領域軟體工程實踐; 6 *==============================================================================*/ 7 8 9 namespace ProgramComponent 10 { 11 using System.Collections.Generic; 12 13 /// <summary> 14 /// Program book mark Manager.<see cref="System.Collections.Dictionary{BookmarkName,ProgramBookmark}"/> 15 /// </summary> 16 public class ProgramBookmarkManager : Dictionary<string, ProgramBookmark> 17 { 18 /// <summary> 19 /// Add programbookmark and instant next programbookmark. 20 /// </summary> 21 /// <param name="bookmark"><see cref="ProgramComponent.ProgramBookmark"/></param> 22 public void Add(ProgramBookmark bookmark) 23 { 24 base.Add(bookmark.BookmarkName, bookmark); 25 } 26 /// <summary> 27 /// Remove programbookmark. 28 /// </summary> 29 /// <param name="bookmark"><see cref="ProgramComponent.ProgramBookmark"/></param> 30 public void Remove(ProgramBookmark bookmark) 31 { 32 base.Remove(bookmark.BookmarkName); 33 } 34 /// <summary> 35 /// Resume bookmark by bookmarkname. 36 /// </summary> 37 /// <param name="bookmarkName">bookmark name.</param> 38 /// <param name="payload">Continue load.</param> 39 public void Resume(string bookmarkName, object payload) 40 { 41 ProgramBookmark bookmark; 42 this.TryGetValue(bookmarkName, out bookmark); 43 if (bookmark != null) 44 { 45 bookmark.Payload = payload; 46 bookmark.ContinueAt(bookmark); 47 } 48 } 49 } 50 }
書籤管理器基本功能還算簡單,主要的方法Resume是用來恢復指定的書籤的;再來看一下訂單檢查流程呼叫;
1 /*============================================================================== 2 * Author:深度訓練 3 * Create time: 2013-08-10 4 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ 5 * Author Description:特定領域軟體工程實踐; 6 *==============================================================================*/ 7 8 namespace ConsoleApplication1 9 { 10 using System; 11 using ProgramComponent; 12 13 [Serializable] 14 public class OrderCheckFlows 15 { 16 private ProgramBookmarkManager BookmarkManager = new ProgramBookmarkManager(); 17 18 public OrderCheckFlows() 19 { 20 BookmarkManager.Add(new ProgramBookmark("checkPrices", new ProgramBookmarkLocation(CheckOrderPrices))); 21 } 22 23 public void StartCheck() 24 { 25 BookmarkManager.Resume("checkPrices", null); 26 } 27 28 #region business flows 29 public void CheckOrderPrices(ProgramComponent.ProgramBookmark nextCheck) 30 { 31 Console.WriteLine("checkPrices..."); 32 BookmarkManager.Remove(nextCheck); 33 34 BookmarkManager.Add(new ProgramBookmark("checkItems", new ProgramBookmarkLocation(CheckOrderItems))); 35 BookmarkManager.Resume("checkItems", true); 36 } 37 38 public void CheckOrderItems(ProgramComponent.ProgramBookmark nextCheck) 39 { 40 if ((bool)nextCheck.Payload) 41 Console.WriteLine("checkItems..."); 42 else 43 Console.WriteLine("end check items."); 44 45 BookmarkManager.Remove(nextCheck); 46 } 47 #endregion 48 } 49 }
是不是比之前的程式碼好多了,我感覺是好多了,當然還有很大的重構空間;
這裡其實已經可以和鏈式程式設計的機制掛鉤了,我們可以通過給書籤管理器新增N個擴充套件方法來使書籤管理器具有跟鏈式的呼叫;
3】可恢復語句元件(將可恢復語句物件化)
要想把所有的呼叫都拆開來使用鬆散的方式組合,通過使用書籤機制基本上能將所有的方法進行鬆散組合;那麼我們還需要將邏輯語法進行物件化才能做到無死角的鬆散;
什麼叫語句元件,就是將一些原本無法獨立的一些邏輯判斷、迴圈之類的語句物件化,形成更具有物件的元件;
試想一下,如果我們將所有的這些邏輯語法物件化後我們的程式碼中還有精密耦合的程式碼嗎?就算有也應該會很少,是不是很神奇;
其實對 企業應用架構 中的 規約模式 有所瞭解的人應該會比較熟悉這一節的內容,跟規約模式很像,但不是一個東西,側重點不同;語句元件全面的概念是將所有的呼叫都物件化,包括一些輸出、輸入、網路呼叫等等,這樣才是全部的語句元件定義,還記得我們上面的訂單檢查物件嘛,那個也是語句元件之一;
我們來構造可恢復語句元件物件;
ProgramComponent.LanguageComponent.LanguageComponent類程式碼:
1 /*============================================================================== 2 * Author:深度訓練 3 * Create time: 2013-08-10 4 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ 5 * Author Description:特定領域軟體工程實踐; 6 *==============================================================================*/ 7 8 namespace ProgramComponent.LanguageComponent 9 { 10 using System; 11 12 [Serializable] 13 public abstract class LanguageComponent 14 { 15 public abstract void Run(ProgramBookmarkManager mgr); 16 } 17 }
ProgramComponent.LanguageComponent.LanguageComponentBlock類程式碼:
1 /*============================================================================== 2 * Author:深度訓練 3 * Create time: 2013-08-10 4 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ 5 * Author Description:特定領域軟體工程實踐; 6 *==============================================================================*/ 7 8 9 namespace ProgramComponent.LanguageComponent 10 { 11 using System.Collections.Generic; 12 13 public class LanguageComponentBlock : LanguageComponent 14 { 15 List<LanguageComponent> statements = new List<LanguageComponent>(); 16 public List<LanguageComponent> Statements 17 { 18 get { return statements; } 19 } 20 21 public void AddLangugateComponent(LanguageComponent lc) 22 { 23 statements.Add(lc); 24 } 25 26 public override void Run(ProgramBookmarkManager mgr) 27 { 28 29 } 30 } 31 }
ProgramComponent.LanguageComponent.IfElseLanguageComponent類程式碼:
1 /*============================================================================== 2 * Author:深度訓練 3 * Create time: 2013-08-10 4 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ 5 * Author Description:特定領域軟體工程實踐; 6 *==============================================================================*/ 7 8 namespace ProgramComponent.LanguageComponent 9 { 10 using System; 11 using System.Linq; 12 using System.Linq.Expressions; 13 14 public class IfElseLanguageComponent : LanguageComponentBlock 15 { 16 public Func<bool> Exp { get; set; } 17 18 public override void Run(ProgramBookmarkManager mgr) 19 { 20 if (Exp()) 21 base.Statements[0].Run(mgr); 22 else 23 base.Statements[1].Run(mgr); 24 } 25 } 26 }
檢查流程程式碼,OrderCheckFlows\OrderSubmitFlows類程式碼:
1 /*============================================================================== 2 * Author:深度訓練 3 * Create time: 2013-08-10 4 * Blog Address:http://www.cnblogs.com/wangiqngpei557/ 5 * Author Description:特定領域軟體工程實踐; 6 *==============================================================================*/ 7 8 namespace ConsoleApplication1 9 { 10 using System; 11 using ProgramComponent; 12 using ProgramComponent.LanguageComponent; 13 14 [Serializable] 15 public class OrderCheckFlows : LanguageComponent 16 { 17 private ProgramBookmarkManager BookmarkManager = new ProgramBookmarkManager(); 18 19 public OrderCheckFlows(ProgramBookmarkManager bookmarkManager) 20 { 21 this.BookmarkManager = bookmarkManager; 22 BookmarkManager.Add(new ProgramBookmark("checkPrices", new ProgramBookmarkLocation(CheckOrderPrices))); 23 } 24 public override void Run(ProgramBookmarkManager mgr) 25 { 26 this.BookmarkManager = mgr; 27 StartCheck(); 28 } 29 public void StartCheck() 30 { 31 BookmarkManager.Resume("checkPrices", null); 32 } 33 34 #region business flows 35 public void CheckOrderPrices(ProgramComponent.ProgramBookmark nextCheck) 36 { 37 Console.WriteLine("checkPrices..."); 38 BookmarkManager.Remove(nextCheck); 39 40 BookmarkManager.Add(new ProgramBookmark("checkItems", new ProgramBookmarkLocation(CheckOrderItems))); 41 BookmarkManager.Resume("checkItems", true); 42 } 43 44 public void CheckOrderItems(ProgramComponent.ProgramBookmark nextCheck) 45 { 46 if ((bool)nextCheck.Payload) 47 Console.WriteLine("checkItems..."); 48 else 49 Console.WriteLine("end check items."); 50 51 BookmarkManager.Remove(nextCheck); 52 } 53 #endregion 54 } 55 56 [Serializable] 57 public class OrderSubmitFlows : LanguageComponent 58 { 59 private ProgramBookmarkManager BookmarkManager = new ProgramBookmarkManager(); 60 61 public OrderSubmitFlows(ProgramBookmarkManager bookmarkManager) 62 { 63 this.BookmarkManager = bookmarkManager; 64 BookmarkManager.Add(new ProgramBookmark("CheckSubmitPrices", new ProgramBookmarkLocation(CheckSubmitPrices))); 65 } 66 67 public override void Run(ProgramBookmarkManager mgr) 68 { 69 this.BookmarkManager = mgr; 70 StartCheck(); 71 } 72 public void StartCheck() 73 { 74 BookmarkManager.Resume("CheckSubmitPrices", null); 75 } 76 77 #region business flows 78 public void CheckSubmitPrices(ProgramComponent.ProgramBookmark nextCheck) 79 { 80 Console.WriteLine("CheckSubmitPrices..."); 81 BookmarkManager.Remove(nextCheck); 82 83 BookmarkManager.Add(new ProgramBookmark("CheckSubmitItems", new ProgramBookmarkLocation(CheckSubmitItems))); 84 BookmarkManager.Resume("CheckSubmitItems", true); 85 } 86 87 public void CheckSubmitItems(ProgramComponent.ProgramBookmark nextCheck) 88 { 89 if ((bool)nextCheck.Payload) 90 Console.WriteLine("CheckSubmitItems..."); 91 else 92 Console.WriteLine("end check CheckSubmitItems."); 93 94 BookmarkManager.Remove(nextCheck); 95 } 96 #endregion 97 } 98 }
呼叫程式碼:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace ConsoleApplication1 8 { 9 using ProgramComponent; 10 using ProgramComponent.LanguageComponent; 11 12 class Program 13 { 14 static void Main(string[] args) 15 { 16 ProgramBookmarkManager bookmarkManager = new ProgramBookmarkManager(); 17 18 OrderCheckFlows orderCheckFlow = new OrderCheckFlows(bookmarkManager); 19 OrderSubmitFlows submitCheckFlow = new OrderSubmitFlows(bookmarkManager); 20 21 IfElseLanguageComponent languageComponent = new IfElseLanguageComponent(); 22 languageComponent.Exp = () => { return true; }; 23 languageComponent.AddLangugateComponent(orderCheckFlow); 24 languageComponent.AddLangugateComponent(submitCheckFlow); 25 languageComponent.Run(bookmarkManager); 26 27 Console.ReadLine(); 28 } 29 } 30 }
一切都已經被物件化,我們來看一下邏輯;
圖5:
這裡的返回值決定了後面要執行的語句元件的路徑,如果是true,則應該檢查OrderCheckFlows流程;
圖6:
如果是false,則應該檢查OrderSubmitFlows流程;
圖7:
可恢復語句物件模型基本構造完成,當然複雜的問題還需要仔細的去分析設計,這裡只是一個簡單的示例;
3.1】可恢復語句元件管理器(將可恢復語句視為普通的物件成員,IEnumerable<T>語句元件)
跟程式碼書籤管理器一個道理,這裡我們也可以實現一個LanguageComponentManager來對LanguageComponent管理,當然也要看需要不需要;可恢復語句管理器其實有很多文章可以做,因為它是所有語句元件的中心,這對於後面的持久化有很大的用處;
//由於內容比較多且相當抽象,下一篇文章介紹;
3.2】可恢復語句元件執行時(Program CLR)
所有的語句程式碼都已經被物件化,但是在執行時需要一箇中心來管理這些被物件化的語句元件,因為我們要脫離對棧的依賴;一組語句元件是單個示例流程的一部分,但是我們可能會存在很多一起並行執行的流程,所以這是必須要提供的執行時;
//由於內容比較多且相當抽象,下一篇文章介紹;
3.3】可恢復語句邏輯配置(規則的配置)
領域驅動設計在使用規約模式的時候會存在動態配置的需求,可以參見這裡的語句元件模型,讓規約最大化的提供配置;
//由於內容比較多且相當抽象,下一篇文章介紹;
3.4】可恢復語句邏輯傳輸(將可恢復語句物件遠端傳輸)
//由於內容比較多且相當抽象,下一篇文章介紹;
4】DomainModel規則引擎(規則持久化後管理配置)
//由於內容比較多且相當抽象,下一篇文章介紹;
示例DEMO地址:http://files.cnblogs.com/wangiqngpei557/ConsoleApplication3.zip
作者:王清培
出處:http://www.cnblogs.com/wangiqngpei557/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。