教程|中文
主题: 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模式的另一个重要用途。■
VUnHTML5中文学习网 - HTML5先行者学习网VUnHTML5中文学习网 - HTML5先行者学习网