VB.Net中文教程(11) Prototype樣式
2024-07-10 13:05:30
供稿:網友
 
 
主題: prototype樣式
副題: 多形性、接口(interface) 
????????? 內容 ?????????
v 1. 樣式
v 2. 對象之原型(object 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#、java或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.winforms
'-------------------------------------------------------------------------
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 overrides 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 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 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)。就拿vb 程序員來說﹐對象設計者負責設計類別﹐以便誕生出各式各樣之對象﹔對象裝配者能使用現有對象或將之組裝成更大之對象或系統。
 依據上述加菲貓和西施的例子中﹐人們常很自然地拿自己熟悉的例子來描述他所想要的對象﹔亦即經由舉例來說明他心中的對象﹐是較合乎人們生活習慣的。反而較少以類別來描述他心中之對象。因之﹐對象設計者定義好各類別之后也應各誕生一個對象﹐當做例子(prototype) ﹐最好顯示在windows 畫面上。對象裝配者不需要具有「類別」觀念﹐只需用鼠標點取畫面上的prototype 對象﹐就能獲得一個類似的新對象了。如此﹐就可構成美好的分工情形。然而﹐在vb及java等語言中﹐對象皆是由「類別」來產生的。這會造成沖突問題﹕
u 設計者不能隨時誕生對象供裝配者使用﹐因而希望裝配者隨時藉由類別誕生對象。
u 裝配者最好不必具有類別觀念﹐因而不愿意藉由類別來誕生對象。
如何化解這個問題呢﹖答案是﹕使用prototype 樣式。
對象設計者之工作包括:
 1. 設計(定義)一個form類別叫form1。
 2. 設計一個應用架構(application framework)做為「設計者」與「裝配者」分工的基礎。
 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模式的另一個重要用途。■