漫談多態性與Web服務的結合

來源:互聯網
上載者:User
web|web服務

  本文回顧了多態性概念, 示範了XML Web服務。最重要的是本文將教你如何把多態性和Web服務結合起來。

  一、簡介

  你可能知道多態性,或許也知道Web服務。但是跨越Web服務的多態性又是怎樣的呢? 本文回顧了多態性概念, 示範了XML Web服務。最重要的是本文將教你如何把多態性和Web服務結合起來。

  二、多態性

  那些熟悉物件導向編程 (OOP)的讀者應該對多態性非常熟悉,但並不是每個人都熟悉物件導向編程。 如果你是前一個讀者群,可以直接跳到“XML Web服務”一節。 如果你是後者,請繼續閱讀。

  在物件導向程式設計語言出現之前,如果你想要列印不同類型的資料,需要寫多個方法 ,象是PrintInteger(int i),PrintString(string s) 和 PrintFloat(float f) 。也就是說, 你必須通過命名來區別行為和資料類型,因為 OOP語言出現前任一語言象是C,不允許你用相同的名字寫方法, 即使他們的參數類型不同。

  C++的來到實現了方法重載。因此,你可以寫多個方法 , 象是 PrintInteger(int i)、PrintString(string s) 和 PrintFloat(float f),編譯器自會準確調用特定的Print方法。方法重載被一種稱為名稱重整(name mangling)的技術所支援,在這種技術中,編譯器通過把原方法名稱與其參數相結合產生一個獨特的內部名字來取代原方法名稱。 如此,當你調用Print(1)的時候, 編譯器可能在內部用源於參數類型的首碼重新命名Print方法,這樣一來Print(1)可能就變成 i_Print (1) 。

  方法重載僅是多態性的一種情形。 名稱重整是一種支援方法重載的機制。更普遍的情況下,多態性是與繼承相聯絡。 什麼是繼承呢?繼承就是一個新類 (稱為子類) 從被繼承類(稱為父類或超類)取得自身的部分定義同時增加一些自己的新的資訊。 如果你在相同的類中重載方法, 資料類型必須是不同的。如果你在繼承關係下重載方法, 子類與父類的方法可能完全相同,而且名稱重整器產生同樣的重整名稱。

  舉例來說, 假設一個超類定義一個Print(int i)方法而一個從它繼承的子類也定義了一個Print(int i)方法。當你有一個子類的執行個體時,運用多態性調用Child.Print(int);而當你產生一個父類的執行個體時運用多態性調用Parent.Print(int)。這就是繼承多態性:相同的名字和簽字但是類卻不同。

  繼承多態性是通過使用一種與名稱重整相關的另外一種機制實現的。編譯器把方法放置在一個被稱為虛擬方法表(其實是一個方法數組)的地方。每一個方法在VMT中都有一個索引, 如此當Print(int)被調用的時候, 編譯器將被路由到VMT處找尋Print方法和類的內在索引。這樣一來,編譯器就可以調用正確的方法實現。由編譯器負責管理所有的VMT索引和類位移量。

  簡言之,多態性使你能夠用非常相似的名字定義許多方法,這裡的名字往往都是直觀易記的。 OOP編譯器自會根據調用者類理解到底該調用哪個方法。多態性的最大好處之一就是,你不再必須寫下面這樣的代碼了(這裡使用的僅是描述性語言):

If type of arg is integer then
 PrintInteger(arg)
Else if type of arg is string then
 PrintString(arg)
Else if type of args is float then
PrintFloat(arg)


  現在,有了OOP語言,上面的表達只需用一句即可:

Print(arg)


  編譯器的多態機制通過產生一個方法索引(這實際上相當於上面的條件陳述式),自會計算出應調用print方法的哪一個版本。

  要從語言的角度瞭解關於OOP內部工作機制的權威描述,可以參閱Bjarne Stroustrup的《The C++ Programming Language》 (ISBN: 0201700735)(Addison Wesley公司出版)。注意,許多OOP語言使用與C++非常相似的機制。

  三、XML Web服務

  如果你對XML Web服務和它的應用有所瞭解,那麼你對XML Web服務的技巧和引入動機的理解應毫無困難,可以直接跳越到下一節“用Web服務支援多態性”。

  在過去十年左右時間當中, 分布式應用已經變得愈來愈普遍。象許多其他類型的工程學一樣, 軟體行業經曆了發明然後標準化的時期。XML Web服務是一種基於HTTP和XML開放協議的標準,它不獨屬於微軟,但是微軟確實提供了基於.NET架構及其特性的XML Web服務的實現。

  基本的思想是,通過你的編碼給描述Web 服務的類添加WebServiceAttribute,並給該類中的Web方法或者是允許消費者調用的方法加上WebMethodAttribute屬性。微軟的實現技術是,使用反射與代碼產生技術來產生代理類型和代理代碼,這使得調用分布式服務和方法變得容易。除了產生代理代碼之外, .NET架構和Visual Studio還包含一個嚮導來為你代理Web服務和Web方法。

  為產生一個Web服務,運行Visual Studio .NET,然後選擇File-New-Project(本文選工程類型為“Visual Basic Projects”),從新的工程對話方塊的模板列表中選擇“ASP.NET Web Service ”。

  要運行該樣本Web服務及Web方法,去掉工程模板嚮導提供的方法HelloWorld前面的注釋部分,然後運行該方案。要瞭解更多的關於XML Web服務的產生及應用等方面的資訊,可以參看前面的“VB Today”欄目, 特別是《Building Distributed Apps?Use XML Web Service, Not Remoting (Mostly)》(December 2004)。

  四、對基於XML產生的代理類添加多態性支援

  現在,將你的注意力轉向這一文章的目的。

  當你為Web方法定義參數而且返還參數的時候,一個被稱為網路服務發現語言 (WSDL) 的公用程式啟用另一個被稱為 SPROXY的工具。SPROXY使用反射和CodeDOM技術來為你的Web方法中聲明的類型勾划出一個定義,然後為複合類型產生代理類。舉例來說,如果你有一個叫做Person的類,當消費者使用該Web服務時, SPROXY 將會為你產生一個Person類。其優點是,Web服務生產者不必要因消費者要使用他們的代碼而把他們的專有代碼發送給消費者。SPROXY 為他們做了工作。通過使用代理代碼,在商業上的私人生意規則得到保護的同時,仍然實現銷售之目的――讓客戶存取這些規則提供的特性。

  下面的代碼描述了一個Person類和一個產生的Person代理。

  列表 1. 在Web服務背後的Person類

public class Person
{
 private string name;
 public Person(){}

 public Person(string name)
 {
  this.name = name;
 }

 public string Name
 {
  get{ return name; }
  set{ name = value; }
 }

 public string GetUpperName()
 {
  return name.ToUpper();
 }

 public string UpperName
 {
  get{ return GetUpperName(); }
  set{}
 }
}

  列表 2.由 SPROXY所產生的Person類的代理版本

public class Person
{
 /// <remarks/>
 public string Name;
}

  如你所見,沒有一個版本包含任何的私人資訊。網路服務並不自動判斷或者要求你加入自己的技術保護――保留私人的商業規則僅僅是一種副產品罷了,因為消費者拿到的代理類是一個不包含任何方法的結構而已。

  大概SPROXY所能做的就是,反射Web方法的類型並將公用的屬性轉換成公用的域。這對於經由Web服務在客戶和伺服器間發送和接收資料已是足夠了。

  除此之外,強型別集合要被轉換成強型別數組。舉例來說,一個從 System.Collections.CollectionBase(關於強型別集合,請參閱我寫的《Visual Basic .NET Power Coding》(Addison Wesley公司出版)一書)派生的PersonCollection集合將被代理產生為一個數組Person或Person()。矛盾在這裡:如果Person類是一個抽象類別而建立PersonCollection集合的目的是用於包含任何從派生的類如Employee或Customer,那該怎麼辦?沒有一些特別的協助, XML Web服務將會產生一個Person代理類但卻不知道關於Employee或Customer的任何事( 見下面列表3)。這在技術上意味著,如果你返回一個Employees數組以滿足Person集合,消費者程式仍會成功編譯但在運行時會崩潰。 列表3. 集合PersonCollection 和類Person, Employee, and Customer的定義

using System;
using System.Reflection;
using System.Diagnostics;
using System.Xml.Serialization;

namespace BusinessCollections
{
 [Serializable()]
 public class PersonCollection : System.Collections.CollectionBase
 {
  public static PersonCollection CreateNew()
  {
   PersonCollection persons = new PersonCollection();
   persons.Add(new Person("Paul"));
   persons.Add(new Customer("David", "Cadyville"));
   persons.Add(new Employee("Kathy", 50000M));
   return persons;
  }

  public Person this[int index]
  {
   get{ return (Person)List[index]; }
   set{ List[index] = value; }
  }

  public int Add(Person value)
  {
   return List.Add(value);
  }

  public PersonCollection Select(Type type)
  {
   PersonCollection persons = new PersonCollection();
   foreach(Person p in List)
   {
    if( p.GetType().Equals(type))
     persons.Add(p);
   }
   return persons;
  }

  public void Dump()
  {
   foreach(Person person in this)
   {
    Debug.WriteLine(string.Format("Type: {0}",person.GetType().FullName));
    PropertyInfo[] properties = person.GetType().GetProperties();
    foreach(PropertyInfo p in properties)
    {
     try
     {
      Debug.WriteLine(string.Format("Name: {0}, Value: {1}",p.Name, p.GetValue(person, null)));
     }
     catch
     {
      Debug.WriteLine(string.Format("Name: {0}, Value: {1}",p.Name, "unknown"));
     }
    }
   }
  }
 }

 public class Person
 {
  private string name;
  public Person(){}

  public Person(string name)
  {
   this.name = name;
  }

  public string Name
  {
   get{ return name; }
   set{ name = value; }
  }

  public string GetUpperName()
  {
   return name.ToUpper();
  }

  public string UpperName
  {
   get{ return GetUpperName(); }
   set{}
  }
 }

 public class Employee : Person
 {
  private decimal salary;
  public Employee() : base(){}
  public Employee(string name) : base(name){}

  public Employee(string name, decimal salary) : base(name)
  {
   this.salary = salary;
  }

  public decimal Salary
  {
   get{ return salary; }
   set{ salary = value; }
  }
 }

 public class Customer : Person
 {
  private string city;
  public Customer() : base(){}
  public Customer(string name) : base(name){}
  public Customer(string name, string city) : base(name)
  {
   this.city = city;
  }

  public string City
  {
   get{ return city; }
   set{ city = value; }
  }
 }

}

  列表3的重點在於,Employee和Customer派生於Person但Web服務僅僅瞭解Person集合。同時,為示範起見,PersonCollection在靜態方法PersonCollection.CreateNew中建立了Person、 Employee和Customer的執行個體。

  如果你要寫一個返回一個稱為GetPerson的PersonCollection的執行個體的Web方法(參列表4),SPROXY將僅產生一個代理類Person,而且傳回型別變為Person()。

  列表 4: 返回一個PersonCollection執行個體的WebMethod:

[WebMethod]

public PersonCollection GetPeople()
{
 return PersonCollection.CreateNew();
}

  如果列表4是你提供給消費者的全部, 那麼,雖然他們的代碼編譯成功,但是當從Web方法返回的Person數組被初始化時,可憐的消費者將會遇到一個運行時刻錯誤拋出SoapException 。最後,因為你定義了其他一些派生於Person的類,所以你也應該設法使你的Web服務消費者能夠使用這些類型。

  既然你已經瞭解了問題的一切, 修改就很容易了。使用在 System.Xml.Serialization中定義的 XmlInclude屬性來指定另外一些類型――消費者方也需要為它們組建代理程式類 。在類自身的首部加上XmlInclude屬性,用類型對象初始化它――這些類型對象用於每一個需要代理的額外類型。列表5 展示了Web服務的類的首部(該類包含了GetPeople方法)定義情況:

  列表5: 通過使用XmlInclude屬性來確保子類型在Web服務的消費者端已經定義

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Web.Services;
using BusinessCollections;
using System.Xml.Serialization;

namespace Service
{
 /// <summary>
 /// Summary description for Service1.
 /// </summary>

 [XmlInclude(typeof(Customer)),

 XmlInclude(typeof(Person)), XmlInclude(typeof(Employee))]
 public class Service1 : System.Web.Services.WebService
 {
  public Service1()
  {
   //CODEGEN: This call is required by the ASP.NET Web服務
   //Designer
   InitializeComponent();
  }

  [Component Designer generated code]
  // WEB SERVICE EXAMPLE
  // The HelloWorld() example service returns the string
  // Hello World
  // To build, uncomment the following lines then save and build
  // the project
  // To test this web service, press F5

  [WebMethod()]

  public PersonCollection GetPeople()
  {
   return PersonCollection.CreateNew();
  }
 }
}

  為了測試網路服務和網路方法,可以產生一個控制台程式。 選擇“ Project”-“Add Web Reference”,然後添加上面的網路服務。 聲明一個Person()的執行個體,然後啟用Web方法即可。

   五、小結

  本文介紹了結合XML Web服務環境的多態性問題。你可從中瞭解到,代理類型不包含方法,且不必為Web服務消費者產生子類型――如果你不小心很容易破壞繼承關係。

  如果在你的Web方法中包括子型別參數,SPROXY將會為消費者產生那些類型。否則,XmlInclude屬性將指示XML Web服務工具產生額外類型。

相關文章

Alibaba Cloud 10 Year Anniversary

With You, We are Shaping a Digital World, 2009-2019

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。