DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> XML學習教程 >> XML詳解 >> 漫談多態性與Web服務的結合
漫談多態性與Web服務的結合
編輯:XML詳解     
本文回顧了多態性概念, 示范了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服務工具生成額外類型。

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved