我的WCF之旅(7):面向服務架構(SOA)和面向對象編程(OOP)的結合 - Part I
當今的IT領域,SOA已經成為了一個非常時髦的詞,對SOA風靡的程度已經讓很多人對SOA,對面向服務產生誤解。其中很大一部分人甚至認為面向服務將是面向對象的終結,現在的面向對象將會被面向服務完全代替。在開始本Blog之前,我先來談談我對SOA和OO的區別,首先申明,這只是一家之言,歡迎大家批評指正,並且關於SO的談論不是本Blog的主題,只是主題的引子,在這裡只是簡單討論而已。
OO和SO之間具有共同的部分,在運用的領域上存在交集,只有在基於他們交集層面上談論誰是誰非才有意義,下面是我對SO和OO的區別。
OO關注的是如何通過對實體屬性和行為的封裝來重塑模擬軟件環境的真實實體。對SO關注的則是對現實生活的某個任務、功能的實現,說得通俗點,就是如果做好一件事情。所以對象是對Data和Behavior的封裝,而Service則只是體現了一個Functionality。Service是粗粒度的,這樣才能導致Contract的穩定;Service一般是Stateless的,給定什麼樣的輸入,就會有對應的確定的輸出;Service是Autonomous,基本上的實現封裝在Service本身之中,這樣可以使它對其它的Service產生最小的依賴。所以對Service建模是一件不太容易的事情:Service不能太大,否則實現起來不容易,還會增加對外界的依賴;也不能太小,否則整合這個Service的成本會令你望而卻步。
Service是可組合的(Composable),通過整合相關的單獨的,完成某個單個Task或者Activity的小的Service,可以很容易產生一個大的Service,這個Service可以完成一個Functionality的整個流程。比如我們現在把一個Task描述成一個Work flow,如果采用SO的原理來建模,我們可以把組成這個Workflow的單個Activity設計成一個service, 然後根據具體Workflow的特點(比如可能是一個簡單的Sequential workflow,也可能是一個基於State machine的workflow)加上相關的條件很容易的把這些Service整合起來,實際上通過整合集成,我們生成一個新的Service。對OO,由於他面對的是一個的Object,具體在分布式中是一個個的Distributed object,要建立一個Composable object則很難(實際上這樣也沒有什麼意義)。
在OO的概念中,一個Object的屬性往往就是另一個Object,一個Function的實現往往要調用另一個Object的方法,而且這種層次結構可以無限延伸。這樣就會導致真個Object體系變得異常脆弱,經常造成牽一發動全身的狀況。用一個很時髦的詞語來表達的,就是緊耦合(Tightly couple),Object之間的強依賴關系促成了這種緊耦合的、脆弱的體系結構。而OS則不一樣,由於 構成Service體系的單個Service是自治的,Service之間的調用(調用的這個詞語容易讓人聯想到RPC,如果找一個比較貼切的詞語來代替,我覺得consume比較合適)是基於Contract的,Service之間Communication是通過Message的(Message不僅僅是Data的封裝,還是對整個調用的描述,同時基於XML的Message描述和Message的傳遞都是符合一個開放的標准的),所有這些成就了SO的松耦合(Loosely couple)。
說了這麼多,可能大家都覺得我都是在贊揚SO,都貶低OO。其實不然,上面所說的3個方面都是在講應用的構建,而不是具體的編程模式。我想表達的是,曾經盛行的基於OO的理論,在企業應用構架方面,已經不能滿足我們的需要了,我們迫切希望一種全新的理論來指導我們進行企業應用的構架和集成,而這個理論非SO不可。
而在編程模型層面,OO仍然是不可替代的編程模式。所以OO應用於Programming,而SO則更多地運用在Architecture。既然是這樣,我們必須有一種調和劑來調和這兩個運用不同原理的兩個層面的差異,實現他們之間的無縫的結合。比如如何來對繼承,多態,重載等基於OO行為的支持。在這方面,WCF為我們提供了很好的解決方案。所以我說WCF不但是為基於SOA的應用架構提供了技術支持,還通過相關的機制完成我們提出的這個“調和劑”的使命。
在上一篇文章[原創]我的WCF之旅(5):面向服務架構(SOA)對面向對象編程(OOP)的支持——如何實現Service Contract的重載(Overloading)中,我們談到了WCF如何實現了對Overloading的支持,在這裡我們通過一個Sample來討論WCF對繼承的支持。這個Sample中,我們通過一個WCF Service實現了提供天氣信息的功能,或者說,我們實現了一個用作天氣預報的WCF Service。
1.我們照例來看看真個Solution 的結構:
整個Solution由以下4個project構成:
Artech.InheritanceHierarchy.BusinessEntity:這個Project通過定義的Class封裝了在Client和Service端傳遞的數據,在本例中,我們定義了兩個Class:BasicWhetherInfo和WindInfo,他們分別表示Client通過Service獲得的基本天氣情況和刮風的情況。
Artech.InheritanceHierarchy.Service:這個Project是我們的Service,包括Service Contract和Service 本身。我們定義兩個用作天氣預報的Service:SimpleWhetherForecast和FullWhetherForecast,前面一個只用返回簡單的天氣狀況的數據(Conditions和Temperature),FullWhetherForecast在SimpleWhetherForecast基礎上增加了提供風向和風速的數據。
http://localhost/Artech.InheritanceHierarchy: 一個WCF Service Website,以IIS的方式對Service進行Host。
Artech.InheritanceHierarchy.Client: Client端。
2.定義Artech.InheritanceHierarchy.BusinessEntity
BasicWhetherInfo.cs:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
namespace Artech.InheritanceHierarchy.BusinessEntity
{
[DataContract]
[KnownType(typeof(WhetherConditions))]
public class BasicWhetherInfo
{
private WhetherConditions _condition;
private double _temperature;
public BasicWhetherInfo(WhetherConditions condition, double temperature)
{
this._condition = condition;
this._temperature = temperature;
}
[DataMember]
public WhetherConditions Condition
{
get { return _condition; }
set { _condition = value; }
}
[DataMember]
public double Temperature
{
get { return _temperature; }
set { _temperature = value; }
}
public override string ToString()
{
return string.Format("Conditions: {0}; Temperature: {1}", this._condition, this._temperature);
}
}
public enum WhetherConditions
{
Clear,
Cloudy,
Overcost,
Rainy
}
}
BasicWhetherInfo包含連個字段/屬性:Condition和Temperature。屬於Condition不屬於基元(Primitive type)所以我們需要添加 [KnownType(typeof(WhetherConditions))]Attribute(由於跨AppDomain的數據傳遞要求傳遞的數據先輩Serialization。對於.NET中定義的Primitive type,比如string,int以及其他一些常用的類型,比如datetime,WCF具有一套默認的序列化機制,但是對於另外一些類型,Serializor在執行Serialization的時候 需要獲得相關類型的Metadata的信息,WCF通過KnownType attribute向Serializor提供Metadata的信息。)。WindInfo中的Direction屬性也是一樣的原理。
WindInfo.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
namespace Artech.InheritanceHierarchy.BusinessEntity
{
[DataContract]
[KnownType(typeof(WindDirection))]
public class WindInfo
{
private WindDirection _direction;
private string _speed;
public WindInfo(WindDirection direction, string speed)
{
this._direction = direction;
this._speed = speed;
}
[DataMember]
public WindDirection Direction
{
get { return _direction; }
set { _direction = value; }
}
[DataMember]
public string Speed
{
get { return _speed; }
set { _speed = value; }
}
public override string ToString()
{
return string.Format("Direction: {0}; Speed: {1}", this._direction, this._speed);
}
}
public enum WindDirection
{
East,
South,
West,
North,
Northeast,
SouthEast,
Northwest,
Southwest
}
}
3.定義Service:Artech.InheritanceHierarchy.Service
我們首先來定義Service Contract
ISimpleWhetherForecast.cs:
using System;
using System.Collections.Generic;
using System.Text;
using Artech.InheritanceHierarchy.BusinessEntity;
using System.ServiceModel;
namespace Artech.InheritanceHierarchy.Service
{
[ServiceContract]
public interface ISimpleWhetherForecast
{
[OperationContract]
BasicWhetherInfo GetBasicWhetherInfo(string postalcode);
}
}
IFullWhetherForecast.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using Artech.InheritanceHierarchy.BusinessEntity;
namespace Artech.InheritanceHierarchy. Service
{
[ServiceContract]
public interface IFullWhetherForecast:ISimpleWhetherForecast
{
[OperationContract]
WindInfo GetWindInfo(string postalcode);
}
}
我們定義了連個Interface作為Service Contract。其中IFullWhetherForecast繼承ISimpleWhetherForecast。這裡需要注意的是雖然IFullWhetherForecast繼承ISimpleWhetherForecast,但是運用在ISimpleWhetherForecast中的ServiceContract Attribute卻不能被IFullWhetherForecast使用,這是因為在定義System.ServiceModel. ServiceContractAttribute, 把運用在它之上的AttributeUsage的Inherited設為false, 導致它不能運用到派生的Class上面:
using System;
using System.Net.Security;
namespace System.ServiceModel
{
[AttributeUsage(1028, Inherited = false, AllowMultiple = false)]
public sealed class ServiceContractAttribute : Attribute
{
public ServiceContractAttribute();
public Type CallbackContract { get; set; }
public string ConfigurationName { get; set; }
public bool HasProtectionLevel { get; }
public string Name { get; set; }
public string Namespace { get; set; }
public ProtectionLevel ProtectionLevel { get; set; }
public SessionMode SessionMode { get; set; }
}
}
我們接著為這兩個Service Contract定義對應的Service。
SimpleWhetherForecastService:
using System;
using System.Collections.Generic;
using System.Text;
using Artech.InheritanceHierarchy.BusinessEntity;
namespace Artech.InheritanceHierarchy.Service
{
public class SimpleWhetherForecastService:ISimpleWhetherForecast
{
ISimpleWhetherForecast Members#region ISimpleWhetherForecast Members
public BasicWhetherInfo GetBasicWhetherInfo(string postalcode)
{
BasicWhetherInfo info = new BasicWhetherInfo(WhetherConditions.Overcost, 23);
return info;
}
#endregion
}
}
為了代碼的重用,我們讓FullWhetherForecastService繼承自SimpleWhetherForecastService,這樣我們就不必重新定義GetBasicWhetherInfo方法了。
FullWhetherForecastService.cs:
using System;
using System.Collections.Generic;
using System.Text;
using Artech.InheritanceHierarchy.BusinessEntity;
namespace Artech.InheritanceHierarchy.Service
{
public class FullWhetherForecastService:SimpleWhetherForecastService,IFullWhetherForecast
{
IFullWhetherForecast Members#region IFullWhetherForecast Members
public WindInfo GetWindInfo(string postalcode)
{
WindInfo info = new WindInfo(WindDirection.Northwest, "12km/h");
return info;
}
#endregion
}
}