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

教程|中文

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

时间: 2024-10-26 05:47:28

VB.Net中文教程(11) Prototype样式的相关文章

VB.Net中文教程(2) Composite样式

教程|中文 1. 从Whole-part关系谈起 回想传统的软件师﹐常甚专注于撰写程序(procedure) 来处理某些资料(data)﹐较少关心软件的整体结构(architecture).在现在的OO软件中﹐把资料及其相关的程序结合在一起﹐封装(encapsulate) 在对象之中.软件师在使用对象时﹐通常把对象视为黑箱(black-box) ﹐不会关心于对象内部之细节﹔因之能专注于对象之间的关系.软件师的主要工作﹐就是在于建立对象之间的互助合作(collaboration) 关系﹐为对象安排

Expression Blend实例中文教程(11)

Expression Blend实例中文教程(11) - 视觉管理器快速入门Visual State Manager(VSM) Visual State Manager,中文又称视觉状态管理器(简称为VSM),是Silverlight 2中引进的一个概念 .通过使用VSM,开发人员和设计人员可以轻松的改变项目控件的视觉效果,在项目中VSM主要用于创建自 定义控件以及控件模板.为了能够打造个性绚丽的Silverlight项目,学习掌握VSM是非常必要的.本文将 介绍VSM的快速入门知识以及VSM在

VB.Net中文教程(8) 对象(Object)基本概念

object|对象|概念|教程|中文 主题: 对象(Object)基本概念 ???????????? 内容 ????????????v 1. 何谓「对象导向」? v 2. 认识与寻找对象v 3. 对象之分类与组织v 4. 类别之设计 v 5. 对象之行为v 6. 讯息与运算v 7. 类别之定义 v 8. 着手写 VB程序 大地运转.花开花谢.及枫叶飘零 ...... 是自然界对象之行为.对象行为交互作用﹐造成多采多姿的大自然.软件的对象是自然界对象的抽象表示,软件就逼真地表达自然界的实际景象﹐于

VB.Net中文教程(12) 共享成员(Shared Member)

教程|中文 主题: 共享成员(Shared Member) ?????? 内容 ??????v 1. 共享资料成员v 2. 共享程序成员 您已经习惯像 New Employee("Tom", 25)这样的指令了,看到这个指令可以想向它是:Employee.New("Tom", 25),于是不难想象到,原来类别也是对象!这个类别对象(Class Object)接到New()讯息时,就去诞生一个对象,原来类别对象就是妈妈对象(Meta Object)!妈妈是小孩共有的,

VB.Net中文教程(13) Whole-Part关系

教程|中文 主题: Whole-Part关系 ?????????? 内容 ??????????v 1. 对象Whole-Part关系v 2. 组合/部分关系v 3. 包含者/内容关系v 4. 集合/成员关系 1. 对象Whole-Part关系 类别继承(Class inheritance)和对象组合(Object composition)是软件再使用(Reuse)的两大法宝.类别继承就是建立父.子类别之关系﹔例如﹐「学生」可分为「大学生」.「中学生」和「小学生」三类别﹐其继承关系图标如下﹕ 图1

VB.Net中文教程(1) 类别与封装性

封装|教程|中文 1. 类别的「程序成员」(Procedure Member) 类别 (Class)之任务是把资料(Data)和程序(Procedure)组织并封装起来.类别告诉计算机﹕「其对象应含有那些资料.应含有那些程序裨处理外界传来之讯息」.类别须详细说明它的资料及程序﹐我们称此资料是类别之「资料成员」(Data Member) ﹔而称此程序是类别之「程序成员」(Procedure Member).有关类别内容之叙述﹐就是所谓的类别定义(Class Definition).类别定义之格式为

VB.Net中文教程(3) 继承与封装性

封装|继承|教程|中文 1. 继承与封装性(Encapsulation)1.1 公用与私有数据 前面已介绍「封藏性」(Encapsulation) 之观念.即是﹕类别内所定义之资料成员﹐只限于程序成员才能存取之.现在所面临之问题为﹕子类别能否直接存取父类别之资料呢﹖就如同﹕儿女从父母亲继承了财产﹐但能否取用或卖掉继承而来的财产呢﹖如果可以﹐显然违背了「封藏性」之理想.如果不行﹐显然带给程序员莫大之限制.理论上﹐百分之百的封藏性最为完美﹔但应用上﹐若给予子类别若干优待﹐能提高程序之弹性及效率.为了

VB.Net中文教程(4) 类别继承(Inheritance)关系

继承|教程|中文 1. 类别之继承 类别之间﹐有些互为独立﹐有些具有密切关系.兹介绍类别间常见的关系──「父子」关系﹔由于儿女常继承父母之生理或心理特征﹐所以又称此关系为「继承」(Inheritance) 关系.类别间之密切关系﹐把相关的类别组织起来﹐并且组织程序内之对象.若程序内之对象毫无组织﹔呈现一片散沙﹐就不是好程序.完美之VB程序﹐必须重视类别间之关系﹐对象是有组织的. 如果 A类别「继承」 B类别﹐则称 A为「子类别」(Subclass)﹐也称B 为「父类别」(Superclass)﹐

VB.Net中文教程(5)程序多重定义

程序|教程|中文 1. 程序成员的多重定义 「程序多重定义」(Function Overloading)又称为「程序重复定义」.它让对象更具弹性﹐能处理多样化之讯息.这观念源于日常生活经验.例如﹐我们常说﹕ ◎ 猫 玩 绣球 ◎ 猫 玩 老鼠 ◎ 猫 玩 鱼 猫玩绣球与玩老鼠之玩法不尽相同.但何以使用同一动词──「玩」呢﹖也许人们认为其目的是一致的﹕猫获得快乐.上述的「猫」为类别﹐而某只猫是对象.例如﹕加菲猫是对象﹐它可接受讯息── 其中﹐「玩」代表着动作和过程﹐而绣球.老鼠及鱼则是「玩」之对象