WCF分布式開發(fā)步步為贏(6):WCF服務(wù)契約繼承與分解設(shè)計
上一節(jié)我們學(xué)習(xí)了WCF分布式開發(fā)步步為贏(5)服務(wù)契約與操作重載部分。今天我們來繼續(xù)學(xué)習(xí)WCF服務(wù)契約繼承和服務(wù)分解設(shè)計相關(guān)的知識點(diǎn)。WCF服務(wù)契約繼承有何優(yōu)勢和缺點(diǎn)?實(shí)際項目里契約設(shè)計有什么原則和依據(jù)?面向?qū)ο蟮脑O(shè)計經(jīng)驗有何值得借鑒的地方?這里我們會一一給出詳細(xì)的介紹。本文首先介紹的是WCF服務(wù)中契約繼承的一些概念、例子代碼分析,其次來講解服務(wù)契約的設(shè)計問題。首先介紹的也是進(jìn)行服務(wù)設(shè)計的必要性,服務(wù)設(shè)計的原則,示例代碼分析。最后是全文的總結(jié)部分。結(jié)構(gòu)如下:【1】OO面向?qū)ο笤O(shè)計原則,【2】服務(wù)契約繼承,【3】服務(wù)契約分解概念,【4】服務(wù)契約分解原則,【5】服務(wù)契約分解代碼分析,【6】總結(jié)。
【1】面向?qū)ο笤O(shè)計原則OO:
這里我們有必要先回顧一下面向?qū)ο蟮慕?jīng)典的設(shè)計原則。這些設(shè)計原則對我們WCF服務(wù)契約的設(shè)計來說有重要的參考價值。服務(wù)契約實(shí)際利用了接口來定義實(shí)現(xiàn),語法類似,WCF框架也是基于現(xiàn)有的語言體系,對此擴(kuò)展了編程模型,比如增加了屬性設(shè)置機(jī)制等。如果你曾經(jīng)接觸過OO面向?qū)ο蟮倪@些概念,那么這些設(shè)計原則理解起來不會困難。很多編程書籍里都會有介紹,設(shè)計模式相關(guān)書籍里會有比較詳細(xì)的介紹。這里介紹幾個主要的概念,為下文的繼承和設(shè)計WCF服務(wù)契約部分作鋪墊:
<1>單一職責(zé)原則(SRP): 一個類應(yīng)該僅有一個引起它變化的原因。
<2>開放封閉原則(OCP): 類模塊應(yīng)該是可擴(kuò)展的,但是不可修改(對擴(kuò)展開放,對更改封閉)。
<3>Liskov 替換原則(LSP): 子類必須能夠替換它們的基類。
<4> 依賴倒置原則(DIP): 高層模塊不應(yīng)該依賴于低層模塊,二者都應(yīng)該依賴于抽象。 抽象不應(yīng)該依賴于實(shí)現(xiàn)細(xì)節(jié),實(shí)現(xiàn)細(xì)節(jié)應(yīng)該依賴于抽象。
<5>接口隔離原則(ISP): 不應(yīng)該強(qiáng)迫客戶程序依賴于它們不用的方法。
【2】服務(wù)契約繼承:
服務(wù)契約的定義和接口定義類似,接口可以繼承與多個接口。但是WCF契約屬性是不支持繼承的。由于WCF框架自身的問題,不支持契約屬性的繼承,因此這給我們服務(wù)契約屬性的聲明和使用卻有不少限制。在使用契約繼承屬性的過程中腰注意服務(wù)端契約的屬性繼承問題,此外就是客戶端添加服務(wù)引用后,無法還原服務(wù)端契約層級的關(guān)系,所有的操作契約由一個契約類封裝。因此實(shí)際編程我們要兼顧到兩個方面的情況。
【2.1】服務(wù)端契約層級:
接口支持繼承。但ServiceContract特性不支持繼承的,我們查看其實(shí)現(xiàn)代碼可以知道Inherited = false,即不支持繼承,部分代碼如下:
public sealed class ServiceContractAttribute : Attribute
{

}
因此在定義多層服務(wù)契約接口的時候,我們必須在每層接口上標(biāo)記ServiceContract屬性,以支持WCF服務(wù)契約屬性。
示例代碼如下:
[ServiceContract(Namespace = "http://www.rzrgm.cn/frank_xl/")]
interface IVehicle
{
}
//接口繼承關(guān)系不支持ServiceContract繼承
[ServiceContract(Namespace = "http://www.rzrgm.cn/frank_xl/")]
interface ITruck : IVehicle
{
}
貨車服務(wù)類能夠?qū)崿F(xiàn)整個WCF契約層級接口,我們這里實(shí)現(xiàn)了Run跑和Carry運(yùn)輸貨物的契約,代碼如下:
public class WCFServiceTruck : ITruck
{
//實(shí)現(xiàn)接口定義的方法
public string Run()
{
Console.WriteLine("Hello! ,This an inherite demo
");return "Hello! Truck is running
";}
//實(shí)現(xiàn)接口定義的方法
public string Carry()
{
Console.WriteLine("Hello! ,This an inherite demo
");return "Hello! Truck is carrying
";}
}
宿主可以為契約層級最底層的接口公開一個單獨(dú)的終結(jié)點(diǎn),配置文件設(shè)置代碼如下:
<endpoint
address="http://localhost:9003/WCFServiceTruck"
binding="wsHttpBinding"
contract="WCFService.ITruck">
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:9003/"/>
</baseAddresses>
</host>
</service>
【2.2】客戶端契約層級:
客戶端添加服務(wù)端數(shù)據(jù)引用,導(dǎo)入一個服務(wù)終結(jié)點(diǎn)的元數(shù)據(jù)時,反序列化生成的客戶端契約將不再維持原來的層級關(guān)系。一個單獨(dú)的契約,名稱為終結(jié)點(diǎn)公布的契約名。含了層級中繼承相關(guān)所有接口定義的操作契約。OperationContract特性中的Action與ResponseAction屬性,可以保留原來定義每個操作的契約名。我們要想恢復(fù)服務(wù)端契約繼承的層級關(guān)系,客戶端可以手工修改代理以及導(dǎo)入契約的定義,恢復(fù)契約層級。手動恢復(fù)方式其實(shí)帶給我們很多靈活性,但是也增加了工作量和復(fù)雜度。實(shí)際項目里一般接觸不多,這里就不詳細(xì)介紹,需要的話可以查閱相關(guān)的資料。
【2.2】服務(wù)契約分解概念:
下面我們繼續(xù)講解服務(wù)契約設(shè)計的一些概念知識。其實(shí)服務(wù)契約的設(shè)計在WCF分布式應(yīng)用項目中屬于比較重要的部分。服務(wù)契約的設(shè)計和實(shí)現(xiàn)相對來多比較復(fù)雜,除了注意已有的設(shè)計原則之外還要注意WCF契約相關(guān)的特性。面向服務(wù)分析與設(shè)計的屬于一個較新的領(lǐng)域。實(shí)際的服務(wù)分析和設(shè)計我們還是借助于已有的經(jīng)驗和原則,來指我們更好地設(shè)計服務(wù)契約。這也是本節(jié)給出一個面向?qū)ο笾匾O(shè)計原則的原因。
因為WCF服務(wù)契約的定義借助現(xiàn)有的編程語言如C#,契約設(shè)計實(shí)際首先就是對服務(wù)接口的設(shè)計。我們應(yīng)該如何設(shè)計服務(wù)接口?如何知道服務(wù)接口中應(yīng)該定義哪些操作?每個接口又應(yīng)該包含多少操作?等等都是我們必須考慮的問題。Service Contract Factoring就是要考慮服務(wù)接口的分解問題。在面向服務(wù)的應(yīng)用程序中,可重用的基本單元就是服務(wù)接口。因此如何設(shè)計服務(wù)接口就是重中之重。
【4】服務(wù)契約分解原則:
這里我們設(shè)計服務(wù)接口時候即遵循單一職責(zé)和接口隔離等原則,又要考慮系統(tǒng)的開發(fā)成本。合理的接口是專業(yè)的、松耦合的、規(guī)則化和可重用的接口。這些優(yōu)勢同樣有利于整個系統(tǒng)的松耦合和可重用等特性??偟膩碚f,契約分解的目的就是使接口包含的更少操作。
如果我們定義了太多的細(xì)粒度服務(wù)接口,雖然它們易于實(shí)現(xiàn),但集成它們的代價太高。如果我們僅定義了一個復(fù)雜的服務(wù)接口,雖然集成的成本會降低,但卻接口的實(shí)現(xiàn)和可維護(hù)性較差。我們設(shè)計面向服務(wù)的系統(tǒng)時,需要平衡兩個影響系統(tǒng)的因素,接口成本和集成成本。參見下圖。

系統(tǒng)服務(wù)的代價為實(shí)現(xiàn)的代價與集成的代價的綜合。上圖顯示了最小代價與服務(wù)接口規(guī)模和數(shù)量之間的關(guān)系。設(shè)計良好的系統(tǒng)應(yīng)該在系統(tǒng)集成成本和契約接口設(shè)計實(shí)現(xiàn)成本之間作何平衡點(diǎn),達(dá)到系統(tǒng)整體開發(fā)成本的降低。
【5】服務(wù)契約分解代碼分析:
這里我們來講解一個簡單的服務(wù)契約設(shè)計的例子。這里我們還繼續(xù)使用交通車為例子進(jìn)行講解。
我們首先定義一個接口交通工具IVehicle,定義了如下:
interface IVehicle
{
//操作契約,跑,開的契約
[OperationContract]
string Run();
//操作契約,拉人、載人的契約
[OperationContract]
string Take();
//操作契約,運(yùn)輸貨物的契約
[OperationContract]
string Carry();
}
這里的交通工具接口,分別定義了跑,拉人和載貨三種操作。放在一個接口中。這就違反了接口設(shè)計中的主要的原則ISP接口隔離原則。我們不應(yīng)該強(qiáng)迫服務(wù)繼承他們不需要的操作。接口隔離原則ISP:使用多個專門的接口比使用單一的接口要好。從服務(wù)設(shè)計的角度來說:一個類對另外一個類的依賴性應(yīng)當(dāng)是建立在最小的接口上的。如果服務(wù)類只需要某一些方法的話,那么就應(yīng)服務(wù)類可以繼承相應(yīng)的接口實(shí)現(xiàn)這些需要的方法,而不要實(shí)現(xiàn)不需要的方法。繼承接口意味著作出承諾,服務(wù)類必須實(shí)現(xiàn),也就是所謂的契約的概念。
因此,我們將服務(wù)契約分解為接口層級的方式,通過接口分解和繼承,實(shí)現(xiàn)操作的分離。這里可以重新定義兩個接口貨車ITruck和小轎車ICar,分別定義自己的拉貨Carry();和載人Take()的操作,當(dāng)服務(wù)類需要實(shí)現(xiàn)拉貨操作的時候就繼承避免了接口設(shè)計的職責(zé)的混淆。代碼如下:
[ServiceContract(Namespace = "http://www.rzrgm.cn/frank_xl/")]
interface IVehicle
{
//操作契約,跑,開的契約
[OperationContract]
string Run();
}
//接口繼承關(guān)系不支持ServiceContract繼承
[ServiceContract(Namespace = "http://www.rzrgm.cn/frank_xl/")]
interface ITruck : IVehicle
{
//操作契約,運(yùn)輸貨物的契約
[OperationContract]
string Carry();
}
//接口繼承關(guān)系不支持ServiceContract繼承
[ServiceContract(Namespace = "http://www.rzrgm.cn/frank_xl/")]
interface ICar : IVehicle
{
//操作契約,拉人、載人的契約
[OperationContract]
string Take();
}
【6】總結(jié):
以上就是對WCF服務(wù)繼承和分解設(shè)計相關(guān)知識的介紹,下面簡要做下介紹:
<1>:本文開始講解了OO面向?qū)ο蟮脑O(shè)計原則,作為經(jīng)典的面相對象的設(shè)計經(jīng)驗,也是設(shè)計模式文章里的重要的知識點(diǎn),對WCF服務(wù)設(shè)計有主要的參考價值,比如SRP單一職責(zé)、ISP接口隔離等原則;
<2>:我們應(yīng)該避免設(shè)計過多或者過少的接口,應(yīng)該考慮系統(tǒng)的服務(wù)接口定義實(shí)現(xiàn)的復(fù)雜度和系統(tǒng)服務(wù)集成的成本。綜合起來權(quán)衡,取得一個平衡點(diǎn)。服務(wù)契約成員的最佳數(shù)量(根據(jù)經(jīng)驗總結(jié),僅代表本人觀點(diǎn))應(yīng)介于3到5之間。開發(fā)者在制訂WCF編碼規(guī)范時,應(yīng)該指定一個上限值(例如20)。無論在何種情況,都不能超過該值。
另外避免定義類屬性操作(Property-Like Operation)這樣的定義十分類似C#的屬性訪問器,例如:
string GetCarNumber();
我們不應(yīng)該干涉客戶端屬性訪問,客戶端在調(diào)用抽象操作時,不用關(guān)心具體的實(shí)現(xiàn)細(xì)節(jié),只負(fù)責(zé)調(diào)用操作,而由服務(wù)去管理服務(wù)對象的狀態(tài)。
<3>:WCF服務(wù)設(shè)計原則只是一些參考原則,對實(shí)際的開發(fā)工作起知道作用。包括前面敘述的面相對象的經(jīng)典的設(shè)計原則和設(shè)計模式的寶貴經(jīng)驗。實(shí)際項目中需要結(jié)合自身的實(shí)際情況,實(shí)時調(diào)整和設(shè)計服務(wù)契約接口的設(shè)計,以期設(shè)計去更加合理和高效的服務(wù)契約。最后是本文的參考代碼:Files/frank_xl/WCFServiceFactoringFrankXuLei.rar,下一節(jié)我們繼續(xù)學(xué)習(xí)WCF數(shù)據(jù)契約的知識,請繼續(xù)關(guān)注~。
參考資料:
1.《Programming WCF Services 》,By Juval Lowy;
2.面向?qū)ο笤O(shè)計模式與原則,http://hi.baidu.com/dapengchu/blog/item/0521e6d965525dee38012f5c.html


浙公網(wǎng)安備 33010602011771號