VB.Net中文教程(11) Prototype樣式 (轉)

gugu99發表於2008-01-21
VB.Net中文教程(11) Prototype樣式 (轉)[@more@]

請注意 ......
著作權所有人:物澤事業股份有限公司、
  MISOO技術顧問團隊、物件導向雜誌作者、等。
u本摘自 物件導向雜誌、精通物件觀念與技術等書籍著作。
u本檔案僅供您的參閱,請遵守著作權法,不得做其它商業用途。

 


主題:  樣式
副題:  多形性、介面(Interface) 


?????????  內容  ?????????
v 1. 樣式
v 2. 物件之原型( prototype)
v 3. 以VB落實Prototype樣式
v 4. Prototype樣式之應用----- 之設計與組裝


1. 樣式
  Erich Gamma 等人的名著──"Design Patterns: Elements of Reusable Object-Oriented Software" 含有23個重要的設計樣式(design pattern)。顧名思義﹐「樣式」就是大家可「有樣學樣﹐依樣畫葫蘆」﹐並一而再、再而三地在不同場合﹐重複使用(reuse) 它來解決常見之問題。
  樣式必須常常使用﹐且愈純熟愈好﹐才能隨外界環境(context) 而加以變化﹐才能確實解決問題(problem) 。像孫子兵法、太極拳法皆含有許多樣式(或稱為招式)﹐必須心領神會﹐並實際練習之﹐才能達到爐火純青之地步。其主要原因是﹕單一樣式(招式)常只解決個小問題﹐而大問題可能需要多種樣式混合使用才行。如何將小樣式組合成為大樣式來解決大問題呢﹖這常需一套完整的法則(rule)﹐通稱為「樣式語言」(pattern language)。本文引用Gamma書中的Prototype樣式﹐說明如何以VB的介面來實作之,也讓您更能善用多形性觀念。以下就請您仔細看如何使用Prototype 樣式了。

 
  圖1、Prototype樣式的UML圖


2. 物件之原型 (object prototype)
  人們日常生活中﹐常見下述說法﹕

  「我要養一隻像加菲貓一樣的貓」
  「我將來要娶個美如西施的妻子」
  ......

其中﹐加菲貓和西施皆是prototype (或譯為範例)。當您說上述兩句話時﹐聽者立即能經由prototype 物件(即加菲貓或西施)來了解您心中所欲描述之新物件。在方面﹐使用者可藉prototype 來告訴計算機﹕

  「我要的物件就像這個prototype 物件」

於是﹐計算機依該prototype 物件來造出一模一樣的新物件給使用者。
  回想﹐我們所熟悉的VB、、或C++語言中﹐皆是藉著「類別」來描述物件之特性﹐然後計算機則依類別之描述來造出新物件。這種就通稱為class-based programming ﹔而前者稱為prototype-based programming 。
  隨著﹐軟體零元件(sofrware IC) 觀念的流行﹐prototype-based programming 觀念也愈來愈重要了。既使像VB語言﹐也能支援prototype-based programming 。


3. 以VB落實Prototype樣式
  上圖1是Gamma書中所列出的prototype樣式。下圖2則是個實際的例子。

 
  圖2、繪圖物件的Prototype

  物件設計者從Shape衍生出Circle及Rectangle兩類別,並各誕生1個prototype物件,且存入ShapeList或陣列之中。設計者必須為各類別定義Clone( )函式來誕生新物件,並構成多形性。於是物件裝配者只需呼叫Clone( )就能獲得新物件,而不必具有類別觀念。未來,設計者可從Shape類別衍生出許許多多子類別,並把物件放入ShapeList中,供裝配者使用。
  茲看看如何以VB來落實上圖2的UML:

'ex01.bas
Imports System.ComponentModel
Imports System.Drawing
Imports System.s
'-------------------------------------------------------------------------
Class Shape
  Protected lx, ly As Integer
  Public Sub SetXY(ByVal x As Integer, ByVal y As Integer)
  lx = x
  ly = y
  End Sub
  Public Overridable Sub Draw()
  End Sub
  Public Overridable Function Clone() As Shape
  End Function
End Class

Class Circle
  Inherits Shape
 
  Public Overrs Sub Draw()
  MessageBox.Show("Drawing a Circle at (" + str(lx) + ", " + str(ly) + ")")
  End Sub
  Public Overrides Function Clone() As Shape
  Clone = New Circle()
  End Function
End Class

Class Rectangle
  Inherits Shape
 
  Public Overrides Sub Draw()
  MessageBox.Show("Drawing a Rectangle at (" + str(lx) + ", " + str(ly) + ")")
  End Sub
  Public Overrides Function Clone() As Shape
  Clone = New Rectangle()
  End Function
End Class
'-------------------------------------------------------------------------------------
Class ShapeList
  Private tlist(10) As Shape
  Private counter As Integer
 
  Public Sub New()
  counter = 0
  End Sub
  Public Sub AddShape(ByVal sobj As Shape)
  tlist(counter) = sobj
  counter = counter + 1
  End Sub
  Public Function GetShape(ByVal i As Integer) As Shape
  GetShape = tlist(i)
  End Function
  Public Sub Draw()
  Dim i As Integer
  For i = 0 To counter - 1
  tlist(i).Draw()
  Next
  End Sub
End Class
'-------------------------------------------------------------------------------------
Public Class Form1
  Inherits System.WinForms.Form
  Public Sub New()
  MyBase.New()
  Form1 = Me
  'This call is required by the Win FoDesigner.
  InitializeComponent()
  'TODO: Add any initialization after the InitializeComponent() call
  End Sub
  'Form overrides dispose to clean up the component list.
  Public Overrides Sub Dispose()
  MyBase.Dispose()
  components.Dispose()
  End Sub
#Region " Form Designer generated code "
  ......
#End Region
  Protected Sub Form1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
  Dim list As New ShapeList()
  Dim ps As Shape
  ps = New Circle()
  ps.SetXY(10, 10)
  list.AddShape(ps)
 
  ps = New Rectangle()
  ps.SetXY(50, 50)
  list.AddShape(ps)
 
  ps = list.GetShape(0).Clone()
  ps.SetXY(230, 70)
  list.AddShape(ps)
 
  list.Draw()
  End Sub
End Class

此輸出:
  Draw a Circle at (10, 10)
  Draw a Rectangle at (50, 50)
  Draw a Circle at (230, 70)

  ShapeList類別屬於Client,在設計ShapeList類別時,只能用到Shape類別的資訊而已;在Client(如上述的Form1類別)裡,除了誕生物件時使用到Circle和Rectangle類別名稱之外,也只能用到Shape類別的資訊而已;這讓我們未來能不斷擴充更多子類別,如Square、Triangle等等。為了達到此高度擴充性,需要用到多形性(polymorphism)觀念。所以Shape類別裡建立了多形的基礎:

  Public Overridable Sub Draw()
  End Sub
  Public Overridable Function Clone() As Shape
  End Function
  Draw( )和Clone( )皆是虛擬(virtual)程式,以發揮多型(polymorphism)功能。lx及ly是圖形的左上角座標。SetXY( )可改變lx及ly值。ShapeList類別的Draw程式用來繪出序列中的各prototype物件圖。
  VB的父類別(superclass)有兩種角色:
  1) 提供一些程式給子類別繼承
  2) 作為各子類別的共同介面(Interface)

上述程式是兩者合一的落實途徑。如果您落實到分散式(Distributed)環境裡,則宜將上述兩項角色分離並各別落實之。此時必須使用VB的Interface機制了。例如上述程式相當於:

'ex02.bas
Imports System.ComponentModel
Imports System.Drawing
Imports System.WinForms
'----------------------------------------------------
Interface IShape
  Sub Draw()
  Function Clone() As IShape
  Sub SetXY(ByVal x As Integer, ByVal y As Integer)
End Interface

Class Shape
  Protected lx, ly As Integer
  Protected Sub SetXY(ByVal x As Integer, ByVal y As Integer)
  lx = x
  ly = y
  End Sub
End Class

Class Circle
  Implements IShape
  Inherits Shape
 
  Public Sub Draw() Implements IShape.Draw
  MessageBox.Show("Drawing a Circle at (" + str(lx) + ", " + str(ly) + ")")
  End Sub
  Public Function Clone() As IShape Implements IShape.Clone
  Clone = New Circle()
  End Function
  Public Sub SetValue(ByVal x As Integer, ByVal y As Integer) Implements IShape.SetXY
  MyBase.SetXY(x, y)
  End Sub
End Class

Class Rectangle
  Inherits Shape
  Implements IShape
 
  Public Sub Draw() Implements IShape.Draw
  MessageBox.Show("Drawing a Rectangle at (" + str(lx) + ", " + str(ly) + ")")
  End Sub
  Public Function Clone() As IShape Implements IShape.Clone
  Clone = New Rectangle()
  End Function
  Public Sub SetValue(ByVal x As Integer, ByVal y As Integer) Implements IShape.SetXY
  MyBase.SetXY(x, y)
  End Sub
End Class
'------------------------------------------------------------------------------------------
Class ShapeList
  Private tlist(10) As IShape
  Private counter As Integer
 
  Public Sub New()
  counter = 0
  End Sub
  Public Sub AddShape(ByVal sobj As IShape)
  tlist(counter) = sobj
  counter = counter + 1
  End Sub
  Public Function GetShape(ByVal i As Integer) As IShape
  GetShape = tlist(i)
  End Function
  Public Sub Draw()
  Dim i As Integer
  For i = 0 To counter - 1
  tlist(i).Draw()
  Next
  End Sub
End Class
'------------------------------------------------------------------------------------------
Public Class Form1
  Inherits System.WinForms.Form
  Public Sub New()
  MyBase.New()
  Form1 = Me
  'This call is required by the Win Form Designer.
  InitializeComponent()
  'TODO: Add any initialization after the InitializeComponent() call
  End Sub
  'Form overrides dispose to clean up the component list.
  Public Overrides Sub Dispose()
  MyBase.Dispose()
  components.Dispose()
  End Sub
#Region " Windows Form Designer generated code "
  ......
#End Region
  Protected Sub Form1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
  Dim list As New ShapeList()
  Dim pShape As IShape
  pShape = New Circle()
  pShape.SetXY(10, 10)
  list.AddShape(pShape)
 
  pShape = New Rectangle()
  pShape.SetXY(50, 50)
  list.AddShape(pShape)
 
  pShape = list.GetShape(0).Clone()
  pShape.SetXY(230, 70)
  list.AddShape(pShape)
 
  list.Draw()
  End Sub
End Class

此程式輸出:
  Draw a Circle at (10, 10)
  Draw a Rectangle at (50, 50)
  Draw a Circle at (230, 70)

Shape類別專心擔任幕後角色了,Client及ShapeList類別的設計者只看到IShape介面而已,這充分發揮了VB介面的優點。


4. Prototype樣式之應用
  ----- 元件之設計與組裝

  軟體工業逐漸往零元件或稱元件(component) 方向發展﹐未來計算機軟體人員將分為兩大群﹕物件設計者(object designer) 與物件裝配者(object assembler)。就拿程式設計師來說﹐物件設計者負責設計類別﹐以便誕生出各式各樣之物件﹔物件裝配者能使用現有物件或將之組裝成更大之物件或。
  依據上述加菲貓和西施的例子中﹐人們常很自然地拿自己熟悉的例子來描述他所想要的物件﹔亦即經由舉例來說明他心中的物件﹐是較合乎人們生活習慣的。反而較少以類別來描述他心中之物件。因之﹐物件設計者定義好各類別之後也應各誕生一個物件﹐當做例子(prototype) ﹐最好顯示在Windows 畫面上。物件裝配者不需要具有「類別」觀念﹐只需用滑鼠點取畫面上的prototype 物件﹐就能獲得一個類似的新物件了。如此﹐就可構成美好的分工情形。然而﹐在VB及Java等語言中﹐物件皆是由「類別」來產生的。這會造成衝突問題﹕

u  設計者不能隨時誕生物件供裝配者使用﹐因而希望裝配者隨時藉由類別誕生物件。
u  裝配者最好不必具有類別觀念﹐因而不願意藉由類別來誕生物件。

如何化解這個問題呢﹖答案是﹕使用prototype 樣式。

物件設計者之工作包括:
 1. 設計(定義)一個Form類別叫Form1。
 2. 設計一個應用架構(application )做為「設計者」與「裝配者」分工的基礎。
 3. 基於架構,衍生出子類別,如Circle及Rect。
 4. 誕生物件,存入序列ShapList中,如圖3所示。

 
  圖3、組裝的基礎環境

物件裝配者之工作是:
  點取(或使訊息)給序列中的prototype物件,由其呼叫Clone( )來誕生新物件。
例如,讓「裝配者」自螢幕畫面上選取物件。Circle和Rectangle各定義Draw( )及Clone( )達到多型效果,讓「裝配者」使用起來更加方便。Clone( )必傳回一個新物件。Form1.Click()裡的指令-----

  pShape = list.GetShape(0).Clone()
  pShape.SetXY(230, 70)
  list.AddShape(pShape)

  首先,取出序列中的第0個prototype物件,此物件誕生另一個物件,由pShape代表之。在將此新物件存入序列中,如下圖所示:
 


 
  圖4、組裝者依據prototype指示計算機誕生物件

最末將各prototype物件數示於畫面上。
  上述例子中,把ShapeList也歸為物件設計者的掌管工作之一。在實際上,ShapeList常隸屬於「裝配工具」(assembly tools)內的一部份。而裝配工具的設計者,可能既非物件設計者,也非物件裝配者,而是用來協助「裝配者」使其工作更佳。
  由於Clone函式及Draw( )程式的多型性,在加上Clone( )能產生新物件,使得裝配工具設計者,不必顧慮到物件類別之劇增,也不必用到期類別名稱,大大減少工具程式之複雜度,這也是prototype模式的另一個重要用途。■

 


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-997845/,如需轉載,請註明出處,否則將追究法律責任。

相關文章