作者:anders小明
2009年5月7日
需求背景
現(xiàn)在的樣子
如PoEAA中提到的:事務腳本(Transaction Script)和表模型(Table Moduel)模式。
存在問題
事務腳本看到的是零散的數(shù)據(jù),而表模型混合了下文要說領域模型和領域服務界限。
1. 兩者都導致了分析和設計的割裂,領域模型只存在于分析中;數(shù)據(jù)間的內在關系無法通過代碼體現(xiàn);
2. 兩者都無法有效的實現(xiàn)業(yè)務邏輯的具體的差異化和抽象的一致性;導致學習和維護成本的增加;
3. 兩者都帶來了測試上困難,增加了開發(fā)成本;
為何要面向對象?
1. 面向對象是自上而下的開發(fā)方法;這種方式對于迭代增進式的結構化過程來說成本是最低的;
2. 數(shù)據(jù)和業(yè)務邏輯的綁定性,采用面向對象容易操作和溝通(即實施成本低),同時有助于區(qū)分狀態(tài)邏輯和任務邏輯;
3. 業(yè)務邏輯的差異性,同一個業(yè)務邏輯針對差異性數(shù)據(jù)有區(qū)分做法,采用面向對象容易維護;
什么是領域驅動設計
領域驅動設計的提出是由Eric Evans在其《領域驅動設計》一書提出。實質上是一種由內而外的設計方法,俗話說的先中間(模型和服務)后兩邊(界面,數(shù)據(jù)庫以及集成)。
領域驅動設計的優(yōu)勢
傳統(tǒng)的開發(fā)方式:基于數(shù)據(jù)庫的設計開發(fā)。數(shù)據(jù)庫提供的設計模型是表和字段兩種粒度,這兩種粒度有時并不合適于系統(tǒng)設計:
1. 模型的結構化能力
1.1. 同一組件下的設計優(yōu)勢,一個模型可以來自多張表的數(shù)據(jù)聚合而成,一張表可以聚合多個模型;一個邏輯是由幾個固定字段或者非固定字段聚合;模型間的關聯(lián)關系也是使用表無法展示的——外鍵的約束對于系統(tǒng)開發(fā)來說實在太有限了。而這些不論表還是字段粒度都無法支持的。
這里強調一下模型間的關聯(lián)關系,特別是和生命周期相關的聚合和組合關系。關系數(shù)據(jù)庫中所保存的是系統(tǒng)分解后的表示,即關系被分解了而不是被表達了,外鍵對數(shù)據(jù)關系只起到一種約束作用,它對于查詢語句的構建并沒有直接的影響。所有數(shù)據(jù)之間的關系都必須在查詢的時候明確指定出來,即調用者必須擁有數(shù)據(jù)關系的知識,而不是數(shù)據(jù)本身擁有這些知識.在如下的SQL語句中
“select * from a, b where a.fldA = b.fldB and a.fldC = 1 and b.fldD = 2”
其中,a.fldA = b.fldB 可以稱為關聯(lián)條件,而a.fldC=1可以稱作是坐標條件。
SQL的復雜性很大程度上來源于我們頻繁的需要在各處指定完全一樣的關聯(lián)條件而無法把它們抽象成可復用的組分.在ORM所提供的對象空間中,對象之間的兩兩關聯(lián)只要指定一次,就可以在增刪改查等各種操作過程中起到作用,特別是在對象查詢語句中,可以通過兩兩關聯(lián)自動推導出多實體之間的關聯(lián)關系,雖然推導出的結果未必是最優(yōu)化的.
1.2. 采用模型方式容易解決項目的集成問題(兩個組件訪問同一張表的情況)
2. 架構的結構化能力
事務腳本直接訪問到表。換句話說,其架構只有服務+數(shù)據(jù)庫表,這樣的架構下數(shù)據(jù)庫表可以說就是我們的模型。
這里看看在邏輯設計上,領域模型驅動設計對于架構影響
A. 服務, 模型和模型倉庫(Repository),模型的重建和關聯(lián)交由模型倉庫完成,單個數(shù)據(jù)邏輯交給模型處理(支持泛化);
B. 測試的好處;
3. 分析和設計的統(tǒng)一
溝通的問題,客戶關注于其業(yè)務,分析的模型接近于客戶需求,設計也采用模型的方式,避免分析和設計的割裂。有助于開發(fā)人員間,開發(fā)人員和業(yè)務人員以及客戶間的溝通
4. 其它好處
4.1.采用領域模型可以屏蔽持久化信息。持久化設計的表設計是和DBA相關的,DBA對于表設計有權力的。采用模型可以有效隔離各自工作;
4.2. 模型可以通過很多的手段透明的解決性能問題,而采用表做模型導致容易導致性能問題,當然不是沒有辦法解決,一種是通過DataSet的方式,但這樣的切換成本較高。
領域驅動設計的可能
上下文環(huán)境
領域模型存在于系統(tǒng)的各個地方,不過在不同地方有不同的映射實現(xiàn)。在通常的開發(fā)過程中,該映射存在于文檔和開發(fā)人員的腦海中的。當要向客戶展示時,就面臨一個映射的關系。比如要允許客戶可以在線編輯頁面呈現(xiàn)的顯示元素,在規(guī)則定義里使用對象系統(tǒng)時。
基礎平臺
隨著ORM框架的發(fā)展,如hibernate,可以提供繼承多態(tài)等能力,能夠支持關聯(lián)關系,特別是面向對象設計中生命周期相關的聚合關系,使得基于領域模型開發(fā)在技術上具備了可行性。
當前不足
不過ORM在集合上的處理不盡如人意。受限于JDK的集合,不能像DLinq那樣提供集合的過濾和集合函數(shù)操作。使得一些設計,不得不屈從于性能問題,把領域模型的關聯(lián)關系人為斷開。
Ferrum項目或許會是一個可行的方案。
領域驅動設計的技術分析
概述
整體結構
領域分兩個含義:領域模型,領域服務,業(yè)務規(guī)則和Repository;其中模型的持久化,重建和關聯(lián)交由Repository完成;而單數(shù)據(jù)邏輯(依賴自身數(shù)據(jù)信息以及關聯(lián)數(shù)據(jù))則歸于模型(支持泛化);服務則關注于任務處理,包括了多個模型處理,以及其它服務的調用。
如果把一個系統(tǒng)看作是一個機械組件的話,那么領域模型就相當于人的骨架;而流程邏輯相當于骨架上的肌肉;那么控制邏輯就相當于肌肉中的神經(jīng)。
領域模型
概念上,一個領域模型和普通的符合面向對象原則的對象有聲明區(qū)別:領域模型是業(yè)務意義上,承載了業(yè)務數(shù)據(jù)(可以認為所有領域模型是有狀態(tài)對象),從本質上說它直接來源于現(xiàn)實世界,沒有技術層次上的考慮,“符合面向對象原則的對象”是用面向對象方法分析得到的,是基于計算機領域技術的(這樣的對象可以是無狀態(tài)的);但反過來,符合面向對象的對象不一定反映業(yè)務領域的模型。
技術上,領域模型是指那些包含需要被透明持久化的屬性,以及相關業(yè)務邏輯的POJO。一個領域模型包含了這些需要被持久化的業(yè)務數(shù)據(jù),同時還包含了與之相關所有業(yè)務操作(即能操作所有屬于本模型生命周期內的模型數(shù)據(jù)),并且有自己的繼承體系。Martin Fowler認為有了這些就可以稱為是一個領域模型,因此在其PoEAA中的ORM包含了一些不透明的持久化方案。我認為一個真正的領域模型需要一個透明持久化。
注: 領域模型在不同視圖下導致不同的內容。比如一個代理人Agent對象,在Party的視圖下只擁有基本屬性,而在Sale channel視圖下就保存了一些額外信息如:考核記錄,優(yōu)秀率等。
領域服務
領域服務包含的商業(yè)邏輯包含了兩部分:流程邏輯。業(yè)務領域的流程邏輯(即業(yè)務流程)。指一系列的業(yè)務行為,包括維護一個或者多個領域模型。領域服務是一個Unit Of Work模式。
領域模型中邏輯僅包括自身生命周期關聯(lián)數(shù)據(jù)的操作,相當于原子操作;只有領域服務則代表完整的服務操作邊界,例如一個領域服務會包含對一個領域模型的三個調用;這一邊界通常也是事務的控制邊界;
Rod Johnson在《J2EE without EJB》第10章《持久化》里面比較清楚的論述了這個問題:
Workflow methods at the business facade level are still responsible for transaction demarcation, for retrieving persistent objects in the first place, and for operations that span multiple objects, but no longer for domain logic that really belongs to the domain model.
這段話明白無誤的講清楚了領域模型應該包含什么邏輯,不應該包含什么邏輯。領域模型包含的邏輯,這里稱之為“領域邏輯”,這些領域邏輯應該最小化的依賴于DAO,而我們討論的那些需要持久化操作參與的事務性邏輯,這里稱之為“workflow methods”,這些“workflow methods”應該放在“business facade”,而不應該放在領域模型里面。
What logic to put into domain classes versus what to put into workflow controllers should be decided case by case. Operations that are reusable across multiple use case s should normally be part of domain classes. Single use case that are hardly reusable are candidates for controller methods; so are operations that span multiple domain objects.
最后Rod Johnson給出來一個區(qū)分的“business workflow logic”和“領域邏輯”的準則:視具體情況而定,緊密結合領域模型的,可重用度很高的操作可能是領域邏輯,應該放在領域模型里面;比較難重用的控制邏輯方法,特別是可跨越多個領域模型的操作則放在business facade object里面。
業(yè)務規(guī)則
這里單列出來,很多時候業(yè)務規(guī)則是附屬在領域服務中的,但在一些特定項目中,業(yè)務規(guī)則會被單獨維護。
1. 產(chǎn)生一些控制信息,限制或者觸發(fā)某些行為的執(zhí)行(A rule is a declarative statement that applies logic or computation to information values);
2. 產(chǎn)生一些狀態(tài)信息,提供給業(yè)務人員參考操作(A rule results either in the discovery of new information or a decision about taking action.)。
其它技術問題
應用職責角色分層,必然涉及到兩種對象,一種是用于展示信息的結構——VO(邊界外通過編碼方式使用的),一種是DAO對象。這兩種職責角色對象,嚴格的說不算是業(yè)務設計需要關心的。然而卻和系統(tǒng)開發(fā)息息相關。而業(yè)務設計中的變化導致相關的工作量卻是巨大的。
VO對象是為了集成而存在的;其意義是:1. 保護系統(tǒng)的信息邊界,提供一種結構可以使其它系統(tǒng)或者組件通過編碼方式獲取系統(tǒng)內信息的方式;2. 保護系統(tǒng)的事務邊界,領域對象技術上攜帶著持久化信息,通過VO得以屏蔽。常見的VO對象存在于Web層和Domain層。
因此,VO對象的存在只是為了集成而存在,其是否存在的取決于兩個方面1. 集成的設計結構;2. 框架的兩個能力——對象路徑訪問能力以及事務邊界管理。
Domain層VO對象,通常是用于不同領域組件間的交互,但隨著架構的改進,集成代碼獨立存在而不再嵌入到組件內部,組件的邊界問題保護不復存在;更進一步的是,框架提供自動化的接口適配映射能力的增強。因而VO對象失去存在的意義。
Web層VO對象,以SWF為例,早在SWF 1.x時代,框架就提供了豐富的對象路徑訪問能力,但其Web交互是典型的MVC2方式,事務邊界在view的render前關閉,因而導致需要特定的VO對象來避免持久化信息問題;而SWF 2.x時代,view的render是在事務邊界內,VO不再需要。
針對維護Dao對象而產(chǎn)生成本的一種解決方法是:代碼生成。生成的策略分為兩種環(huán)境,開發(fā)環(huán)境和生產(chǎn)環(huán)境。開發(fā)環(huán)境實時動態(tài)生成,可以采用動態(tài)代理機制;而生產(chǎn)環(huán)境要求性能,采用預編譯生成能力。
另外Web層或者UI層,不需要額外的VO對象的另一個理由是:通常Web層的獨立維護的成本大于其復用的價值。在開發(fā)中,Web層需要的信息都來自Domain層,這樣容易出現(xiàn)Web層的VO對象和Domain對象結構一致,雖然這樣便于學習和簡化開發(fā),但導致維護成本的提高,每當頁面需要一個新的屬性就需要改變太多的類,同時Domain對象設計容易限制于Web層,而實踐中Web層的變化更多于Domain層。正確的路徑是很好的維護Domain層,同時不維護Web層。
設計開發(fā)
領域模型的四種類型
領域模型可以分為四種類型:
0. 全局常量對象
1. 長生命周期業(yè)務對象(類似保單對象);
2. 交易過程的事務對象,幾乎沒有生命周期的;
3. 業(yè)務請求對象和業(yè)務反饋對象。這類對象以前沒有識別的,通常和VO混在一起;但是在IAA中以及電信業(yè)的模型是這類對象是獨立存在,并被持久化的;業(yè)務請求對象建立在增量更新上很有用。當然他們也是幾乎沒有生命周期的。
并非所有的業(yè)務系統(tǒng)都擁有這四類對象!相當一部分的業(yè)務系統(tǒng),并沒有顯著的長生命周期對象,因而沒有明確的增量變更操作類型及其規(guī)則,業(yè)務操作是直接更新業(yè)務對象,也就沒有業(yè)務請求以及業(yè)務反饋對象;同時此類業(yè)務系統(tǒng)的事務對象也通常不存在;
這里要額外補充說明的是:
對于業(yè)務請求,每個業(yè)務請求必需記錄下業(yè)務時間;對于業(yè)務處理,每個業(yè)務處理還可能保留一定的人工干預控制信息,也將同生命周期的輸入數(shù)據(jù)一起記錄;對于生命周期,每個生命周期狀態(tài)的變化都可能會有獨立的數(shù)據(jù)需要記錄。
領域模型的級別
不論是那種類型對象,都擁有一個屬性,對象等級;對于保險系統(tǒng)來說,保單對象,產(chǎn)品對象以及組織對象是一級對象,而險種和角色等都是二級對象,其生命周期附屬于一級對象;這點對于設計Repository以及服務粒度都有影響。
領域模型的動靜之分
在系統(tǒng)運行期間,被頻繁建立和更新的稱為“動態(tài)”,而在較長的一段時間內保持穩(wěn)定的稱為“靜態(tài)”。
通常而言,“動態(tài)”的領域模型群通常代表了系統(tǒng)的核心業(yè)務對象。而“靜態(tài)”的領域模型則在業(yè)務上代表了系統(tǒng)的依存關系。
領域模型的分析過程
1. 設計一個貧血的領域模型,包含所有需要被持久化的數(shù)據(jù),除了用例顯示要求的和隱式要求的(根據(jù)業(yè)務分析出需要的輔助數(shù)據(jù),主要是特定的控制信息),以及與其它領域模型的關聯(lián)關系;
2. 考慮是否支持多態(tài)
3. 分析潛在的性能問題,根據(jù)需要人為斷開存在的關聯(lián)關系;
4. 列出相關的邏輯,利用面向對象設計原則,決定哪些邏輯屬于該領域,并加入該對象模型;
5. 分析是否實現(xiàn)該對象的依賴關系注入。
業(yè)務請求對象的虛實之道
業(yè)務請求的概念,與HTTP請求是不同的。為避免誤解,特意加上業(yè)務一詞修飾。所謂虛實是指是否將業(yè)務請求概念實例化。不做實例化的理由時處理簡單;實例化則有助于處理業(yè)務事務控制以及應用賬目模式。一個業(yè)務操作上的業(yè)務請求可能包括多個請求對象,與核心業(yè)務對象對應,例如:在線訂單,就包括了購買物品及其數(shù)量和折扣,支付協(xié)議和發(fā)貨協(xié)議等。
對于沒有實例化業(yè)務請求的情況下,在實際業(yè)務操作時,對每一個HTTP表單的操作都需要一個物理的事務來支持。這樣做的問題是,由于沒有記錄業(yè)務請求,直接操作業(yè)務對象,在做業(yè)務日志時只能記錄操作前以及操作后的信息(既“減肥前,減肥后”);同時跨越多個物理事務,要支持查詢到一次業(yè)務請求所有操作的信息,需要新建一個日志索引或者類似的手段,在業(yè)務開始時獲取注冊一個索引,所有日志操作中引用這個索引,在業(yè)務結束后關閉該索引。雖然如此,也帶來的是業(yè)務上做回退(undo)以及重做(redo)操作的不便。
但是如果實例化業(yè)務請求,就很容易處理這兩樣操作。建立一個業(yè)務請求,同時記錄所涉及的請求數(shù)據(jù)。這樣做的好處是:可以很容易的記錄一些額外的信息;同時可以很容易的支持審批操作(既俗話說的“管帳的不管錢,管錢的不管帳”)。
另外,業(yè)務請求對象附加好處是,由于某個領域模塊的增量操作通常從一個根對象(即一級對象)開始,所依賴的過濾條件可以從業(yè)務請求中加以識別并通過框架提前加載,而領域服務對象的方法接受傳遞對象而不再關心對象的加載工作;同時也可以通過框架處理基本數(shù)據(jù)復制工作,這樣程序只關心關聯(lián)對象的操作。
業(yè)務請求對象比較適合明確增量變化的業(yè)務系統(tǒng),通常這樣業(yè)務系統(tǒng)在處理變更時,規(guī)則和關聯(lián)處理很多;對于全量數(shù)據(jù),采用業(yè)務請求對象得不償失,最終兩個對象設計會趨于一致;對于不確定性變化系統(tǒng),如果應用業(yè)務請求對象會導致捕獲變化數(shù)據(jù)的困難,應該考慮采用更好的業(yè)務組織方式(如SpringSide的設計實現(xiàn),但SS沒有考慮批改的流程和日志)。
不過目前大部分的系統(tǒng)都沒有處理業(yè)務請求實例化,不是所有的業(yè)務操作需要審批,另外實例化的麻煩是,已經(jīng)處理了一個日志對象,再處理一個業(yè)務請求對象總是讓人多少心里有點不爽;
一個可能的持久化方式是:記錄對象路徑及其對應的值;通過自動化的系統(tǒng)掃描,獲取對象路徑結構,以Key和Value的形式記錄在數(shù)據(jù)庫中,可以避免復雜的持久化處理;
Repository(Dao)的設計開發(fā)
Repository是一種特殊的Service,不做任務處理;而是提供模型的持久化,重建和查詢工作。由于企業(yè)應用大都通過數(shù)據(jù)庫實現(xiàn)持久化,因而Repository和傳統(tǒng)的Dao間的集成設計就非常重要。
已知的有三種設計方式:
方式 |
描述 |
優(yōu)缺點 |
1. 兩個接口兩個實現(xiàn)類 |
Repository和Dao各自獨立接口,而通過Repository實現(xiàn)類轉發(fā)請求給Dao實現(xiàn)類 |
這種方式雖然正統(tǒng),但是維護成本太高;一次更新最多要改四處地方 |
2.兩個接口一個實現(xiàn)類 |
Repository和Dao各自獨立接口,一個實現(xiàn)類同時實現(xiàn)兩個接口 |
這種方式就大大簡化維護成本;一次更新最多只改一個接口和一個實現(xiàn)類 |
3. 兩個接口一個實現(xiàn)類 |
與方式2不同是,Dao接口繼承Repository接口,一個實現(xiàn)類實現(xiàn)Dao接口 |
這種方式的維護成本和方式2差不多,但是當接口方法在這個兩個接口間流動時,可以通過開發(fā)工具完成 |
另外Dao實現(xiàn)類也是工作中開發(fā)維護成本較高的一部分,可以通過代碼生成降低開發(fā)成本。已知的是JDBC 4.0規(guī)范和iBatis 3.0的實踐。
領域服務的設計開發(fā)
業(yè)務服務是整個領域設計中另一個重要的元素;業(yè)務服務的如何設計,并無定論,但有原則和分類,最重要的是圍繞著業(yè)務流程設計,而其基礎建立于底層模型自身業(yè)務邏輯的原子操作。
按粒度劃分:
1. 原子服務,業(yè)務服務的原子操作;在產(chǎn)品化設計中,該層次服務應該擁有擴展點和參數(shù)化能力;
2. 組合服務,封裝業(yè)務服務的組合操作;在產(chǎn)品化設計中,擁有參數(shù)化,擴展點,事件和集成能力;
3. 還有一類業(yè)務服務設計是實現(xiàn)于工作流,該層次邏輯關注于系統(tǒng)集成上;在產(chǎn)品化設計中,該層次應該擁有事件;
實際上,原子服務和組合服務的粒度劃分并不具備可操作性;只不過加以標識試圖進一步的分析,并為產(chǎn)品化設計做基礎;
按事務劃分:
1. 事務服務,事務服務和持久化操作有關,提供事務邊界;通常是聚合服務;
2. 計算服務,也算是read-only的事務服務;計算服務的粒度不一定;
由于服務是針對領域,因而事務服務不關注于工作流的流程狀態(tài),只關心相關領域中長生命周期領域模型的生命周期;而計算服務更不關心流程相關,只驗證輸入合法性,做出計算,返回結果,完全是無狀態(tài)的;工作流則關心的是相關領域中的request對象的業(yè)務狀態(tài),對于同一業(yè)務對象的并發(fā)處理,應該通過業(yè)務來控制;
領域服務的運行模式
簡單的說,業(yè)務處理將被細化成處理控制器和具體處理器。在這級別,處理對于請求的響應處理已知的有三種模式:
事件模式(Observer Pattern)、職責鏈模式(Chain of responsibility Pattern)以及數(shù)據(jù)流模式(Pipes and Filters Pattern)。這幾個模式處理的各自不同的場景。其中數(shù)據(jù)流模式很適合需要處理大量數(shù)據(jù)的情況。
以下純粹是個人觀點, 不代表任何組織或社團.
現(xiàn)在流行的編程語言如Java和C#, 大多是面向對象的, 程序的各部分是通過方法調用連在一起, 其編程范式是命令編程, 即使支持其他范式也包裝得很難理解和使用.
面向對象實際上只適合實現(xiàn)抽象數(shù)據(jù)類型, 讓它去完成除此之外的任務確實是勉為其難, 即便能完成也給人不倫不類的感覺, 既不像面向對象編程, 也不像它的前任(過程編程). 這些語言中的對象與物質世界的物質(或對象)很不一致, 它使用方法調用的方式與其他對象進行相互作用, 而這與物質之間的(通過通信或媒介)相互作用是截然不同的, 因此用面向對象無法很確切地模擬現(xiàn)實世界(面向對象思想的初衷), 更不用說準確地為現(xiàn)實世界建立模型.
這幾年流行的web服務和SOA雖使程序間交互更方便, 但它從本質上說還是使用”面向對象+命令編程+方法調用”的思路, 在編程方法論上并沒有實質的進步.
用現(xiàn)在流行的語言實現(xiàn)的完成復雜功能的程序邏輯不清晰, 原因在于”命令編程+方法調用”的設計機制. 這使程序很難模塊化, 副作用無所不在, 因此很難正確實現(xiàn)復雜功能.
現(xiàn)實世界的發(fā)展變化是通過事物間的相互作用實現(xiàn)的, 而這種相互作用用計算機科學的語言來說就是并發(fā)(concurrency). 軟件的本質是什么? 我覺得:軟件總是完成某種功能的,歸根到底是對現(xiàn)實世界的事物間相互作用進行建模. 因此軟件的組成部分間自然就是并發(fā)的關系, 而不是過程調用的關系. 用通信進程來對現(xiàn)實世界的事物間相互作用進行建模是比較合理的. 所以進程應該作為語言的基礎成分, 是軟件的基本組成部分, 而不是只為了提高效率才采用的.
為了使程序能準確地為現(xiàn)實世界建立模型, 從而正確性更高, 結構更合理, 模塊化程度更高, 因此在幾種編程思想或語言的基礎上(見references), 我提出一種新的編程方法論: 面向進程+函數(shù)編程+進程間通信+邏輯編程+約束編程+其他合理的范式(命令編程除外)和一門編程語言ProcessLog (全稱process logic).
ProcessLog只支持一種并發(fā):通信進程. 它就是計算機科學家在上世紀70年代為了克服現(xiàn)在的Java中仍采用的那種并發(fā)方式的缺點而提出的. 它是經(jīng)過充分研究得到的一種理想并發(fā)方式, 看了并發(fā)理論(concurrency)和進程代數(shù)(Process algebra), 就會明白這種并發(fā)方式可解決實際中的各種并發(fā)問題, 用它足夠了.
這里的進程是進程代數(shù)的進程,不是過程,也不是Java中的線程. 看看jcsp或Hoare的CSP(http://www.usingcsp.com/)就明白了.推薦網(wǎng)址:
http://www.cs.kent.ac.uk/projects/ofa/jcsp/,
其中有兩個ppt說得很明白:
"Process Oriented Design for Java: Concurrency for All",
"Communicating Processes, Components and Scaleable Systems".
ProcessLog的語法概要如下:
1 運算符
(1) ? 輸入; c ? x 從輸入端口c或通道c上接收輸入值放到變量x中
(2) ! 輸出; c ! v 把v的值從輸出端口或通道c上輸出
(3) -> 順序進行的事件的先后關系
(4) | b : s 分支
(5) || 進程并行
(6) // 附屬進程
(7) and, or, not 邏輯運算符
(8) 算術運算符和關系運算符 與Java中相同
2 程序的組成成分
(1) Unit 程序單元
(2) Process 進程
(3) Function 函數(shù)
(4) Predicate 謂詞
(5) Channel 通道, 有兩個端口: in 輸入端口, out 輸出端口
(6) OutPort 輸出端口
(7) InPort 輸入端口
3 數(shù)據(jù)結構
(1) List (函數(shù)編程中的List類型, 對List的操作函數(shù)與函數(shù)編程中相同);
(2) Tuple 元組, 同Clean.
(3) Set 集合
沒有數(shù)組
4 進程的定義
Process p1 (OutPort pt1, InPort pt2 ){
pt2?x -> pt1! compute(x) -> p1
}
5 進程間相互作用
(1) 進程并行 process1( c1.out, c2.in)|| process2(c1.in, c2.out)
(2) 附屬進程 (getE: getElements || getR: getReleasedVersion) // X.(in?method -> getE ! method ? elems -> getR ! em ? rem-> … ->X)
6 函數(shù)
[Function] compute(double x)=
| x<=0: x*x+3
| x>0: compute(x-5)* compute(x-3)
函數(shù)只能以事件的方式在進程中使用或在其他函數(shù)中使用, 不能獨立使用.
7 謂詞
/* 建圖 */
Predicate createGraph(t, graph):-
addNode(t, null, ({},{}), graph1),
getDS(t, graph1.ns, tlist),
addList(tlist, t, graph1, graph).
/* 加節(jié)點 */
Predicate addNode(t, null, (ns, es), (ns1, es):-
merge(ns, {t}, ns1).
Predicate addNode(t, upper, (ns, es), (ns1, es1)):-
merge(ns, {t}, ns1),
merge(es, {(upper, t)}, es1).
8 把謂詞轉換為函數(shù)
create(t)= graph
where createGraph(t, graph)
謂詞不能獨立使用也不能在進程中直接使用, 要在進程中使用需要先轉換為函數(shù).
9 程序單元: 包含進程和數(shù)據(jù)類型
Unit PmethodDAO;
interface
Tuple Method;
Process getLastVersion(OutPort pt1 , InPort pt2);
…
implementation
Method=(String id, String name, String version);
Process getLastVersion(OutPort pt1 , InPort pt2){
…
}
指導原則: 程序是由通過通道通信的進程組成的. 數(shù)據(jù)處理和算法用函數(shù)編程實現(xiàn), 如果函數(shù)編程不適用于要處理的問題, 就使用邏輯編程或約束編程.
ProcessLog語言限制了編程的隨意性, 要求只能用進程代數(shù)+函數(shù)編程+邏輯編程的方式編程, 不允許用Java或c#的命令方式編程.
ProcessLog 現(xiàn)在還沒有在機器上實現(xiàn). 我用該語言重寫了實際項目中的一些復雜代碼(原是Java代碼), 證實用它寫的程序確實簡單清晰, 有類似數(shù)學的簡潔美. "7 謂詞"就是其中一部分代碼.
我的想法是: 應先在紙面上規(guī)定它的語法與語義, 再通過使用它編寫一些應用程序來發(fā)現(xiàn)它的不足,再進而改進它, 再實踐, 再改進, 直到它基本完善再在機器上實現(xiàn). 另外, 大家不要把語言分為中國人提出的還是外國人提出的, 科學無國界, 這里不存在狹隘的愛國主義. 我不是那種技術高手, 但我自信我是一個能將理論很好地聯(lián)系實踐的研究者.
希望有識之士和我一起共同發(fā)展這種編程方法論和這門語言.
juwenguang2000@yahoo.com.cn
References
1. CSP http://www.usingcsp.com/
2. JCSP http://www.cs.kent.ac.uk/projects/ofa/jcsp/
3. Clean http://clean.cs.ru.nl/
4. Prolog
5. Delphi
注: 轉載時請注明作者.
說的東西,相當復雜。