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模式的另一个重要用途。■