VB.Net中文教程(6) 母子對(duì)象關(guān)系
2024-07-10 13:01:17
供稿:網(wǎng)友
1. 特殊whole-part關(guān)系
---- 母子對(duì)象關(guān)系
大家已經(jīng)熟悉父子類別關(guān)系﹐也就是「繼承」關(guān)系了。于此說明另一種常見關(guān)系── 母子對(duì)象。一般稱之為「組合/部分」關(guān)系。日常生活中﹐處處可見到這種母子對(duì)象。例如﹐客廳內(nèi)有沙發(fā)、桌子、椅子等等??蛷d是母對(duì)象﹐沙發(fā)、桌子、椅子是子對(duì)象。再如計(jì)算機(jī)屏幕上的窗口內(nèi)有按鈕、選擇表、對(duì)話盒等等。窗口是母對(duì)象﹐按鈕、選擇表是子對(duì)象。于此將說明如何建立母子對(duì)象關(guān)系。有了關(guān)系﹐母子就能互相溝通了。母子對(duì)象之間﹐如何溝通呢﹖也就是說﹐母對(duì)象如何呼叫子對(duì)象的程序呢﹖反過來﹐子對(duì)象如何呼叫母對(duì)象的程序呢﹖欲呼叫對(duì)方的程序﹐必先與對(duì)方建立關(guān)系才行。因之﹐如何建立母子對(duì)象關(guān)系﹐是頂重要之課題﹗
請(qǐng)先看個(gè)例子﹐有兩個(gè)類別──room和desk。若room代表房間﹐desk代表房間內(nèi)的桌子﹐則它們會(huì)誕生母子對(duì)象﹕
通常﹐您會(huì)依房間的大小來決定桌子的大小。因之﹐desk對(duì)象應(yīng)跟room對(duì)象溝通﹐取得房間的大小﹐才決定自己的大小。若有個(gè)room之參考﹐則desk對(duì)象就能跟room對(duì)象溝通了。于是﹐可設(shè)計(jì)下述vb程序:
'ex01.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
class room
protected rsize as double
shared motherobject as room
public sub new()
motherobject = me
end sub
shared function getmother() as room
getmother = motherobject
end function
public function getsize() as double
getsize = rsize
end function
end class
class desk
protected dsize as double
public sub new()
dsize = room.getmother().getsize() * 0.18
end sub
public function getsize() as double
getsize = dsize
end function
end class
'----------------------------------------------------
class myroom
inherits room
private rd as desk
public sub new()
rsize = 100
rd = new desk()
end sub
public sub show()
messagebox.show("room size: " + str(rsize))
messagebox.show("desk size: " + str(rd.getsize()))
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 r as new myroom()
r.show()
end sub
end class
此程序輸出:
room size: 100
desk size: 18
desk類別內(nèi)之指令──
room.getmother().getsize()
就是desk對(duì)象與room對(duì)象之溝通了。myroom類別有個(gè)rd參考﹐指向子對(duì)象﹐如下﹕
母對(duì)象經(jīng)由rd參考來跟子對(duì)象溝通。反過來﹐子對(duì)象經(jīng)由room.getmother()參考跟母對(duì)象溝通。
2. 母子對(duì)象誰先誕生﹖
在vb里﹐有時(shí)候子對(duì)象比母對(duì)象早誕生完成。就像建造房子﹐建到半途﹐改而建造桌子﹐桌子建好了﹐再繼續(xù)把房子建完成。由于這不太合乎人們的日常生活習(xí)慣﹐令人感到困惑﹗在計(jì)算機(jī)軟件上﹐也有類似的沖突。例如﹐在windows 下﹐母窗口建好了﹐才建立窗內(nèi)的按鈕、選擇表等子對(duì)象。但vb有時(shí)并非如此﹗因之﹐如何化解這沖突﹐是個(gè)重要的話題。茲拿上小節(jié)的例子來說明吧﹗該例子中﹐其誕生對(duì)象之過程為﹕
step 1: 先誕生母對(duì)象。
step 2: 接著建立子對(duì)象。
這過程合乎人們的習(xí)慣﹐是美好的。其原因是﹕myroom類別在其建構(gòu)者new()程序里呼叫desk的new()誕生desk子對(duì)象。假若您寫下述程序﹐就出問題了﹗
'ex02.bas
'some error here!
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
class room
protected rsize as double
shared motherobject as room
public sub new()
motherobject = me
end sub
shared function getmother() as room
getmother = motherobject
end function
public function getsize() as double
getsize = rsize
end function
end class
class desk
protected dsize as double
public sub new()
dsize = room.getmother().getsize() * 0.18
end sub
public function getsize() as double
getsize = dsize
end function
end class
'----------------------------------------------------
class myroom
inherits room
private rd as new desk()
public sub new()
rsize = 100
end sub
public sub show()
messagebox.show("room size: " + str(rsize))
messagebox.show("desk size: " + str(rd.getsize()))
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 r as new myroom()
r.show()
end sub
end class
其對(duì)象之建造過程為﹕
step 1: 指令──
dim r as new myroom()
呼叫myroom()建構(gòu)者new()。它再呼叫room()建構(gòu)者new()將room部分建好。
step 2: 執(zhí)行指令──
private rd as new desk()
呼叫desk建構(gòu)者new()﹐建好desk子對(duì)象。此時(shí)會(huì)執(zhí)行指令──
dsize = room.getmother().getsize() * 0.18
step 3: myroom建構(gòu)者new() 繼續(xù)將母對(duì)象建完成。
此時(shí)會(huì)執(zhí)行指令── rsize=100
其中,getsize()程序會(huì)取出rsize 值﹐但那時(shí)還未執(zhí)行rsize=100 指令﹐那來
的rsize 值呢﹖所以出問題了。此程序可能輸出如下錯(cuò)誤結(jié)果﹕
room size: 100
desk size: 0
如何解決上述問題呢﹖常見方法是﹕
把會(huì)出問題的指令﹐從建構(gòu)者程序中提出來﹐放到另一程序里。
例如下述程序:
'ex03.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
class room
protected rsize as double
shared motherobject as room
shared function getmother() as room
getmother = motherobject
end function
public overridable sub create()
motherobject = me
end sub
public function getsize() as double
getsize = rsize
end function
end class
class desk
protected dsize as double
public sub create()
dsize = room.getmother().getsize() * 0.18
end sub
public function getsize() as double
getsize = dsize
end function
end class
'----------------------------------------------------
class myroom
inherits room
private rd as new desk()
public sub new()
me.create()
end sub
public overrides sub create()
mybase.create()
rsize = 100
rd.create()
end sub
public sub show()
messagebox.show("room size: " + str(rsize))
messagebox.show("desk size: " + str(rd.getsize()))
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 r as new myroom()
r.show()
end sub
end class
此程序輸出:
room size: 100
desk size: 18
像指令── dsize = room.getmother().getsize() * 0.18﹐已挪到新設(shè)的create()程序里。待母對(duì)象完全建好了﹐才會(huì)呼叫這create()程序﹐getsize() 就能取得正確值了。myroom的new()呼叫create()程序時(shí)﹐母子對(duì)象皆已建造完成了。create()內(nèi)部依人們的習(xí)慣來設(shè)定對(duì)象之值,例如建立母子對(duì)象之關(guān)系。
如此就不會(huì)出問題了。
new()與create()分離之后,myroom類別里的指令:
class myroom
inherits room
private rd as new desk()
public sub new()
me.create()
end sub
........
也能寫為:
class myroom
inherits room
private rd as desk
public sub new()
rd = new desk()
me.create()
end sub
........
只要確保desk類別的指令──
dsize = room.getmother().getsize() * 0.18
是在myroom類別的指令──
rsize = 100
之后執(zhí)行即可了。
上述的子對(duì)象是透過shared 程序來取得母對(duì)象的參考值﹐然后才跟母對(duì)象溝通。如果不透過shared程序,也可以采取下述方法:
'ex04.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
class room
protected rsize as double
public overridable sub create()
end sub
public function getsize() as double
getsize = rsize
end function
end class
class desk
protected dsize as double
protected mymother as room
public sub create(byval mo as room)
mymother = mo
dsize = mymother.getsize() * 0.18
end sub
public function getsize() as double
getsize = dsize
end function
end class
'----------------------------------------------------
class myroom
inherits room
private rd as desk
public sub new()
rd = new desk()
me.create()
end sub
public overrides sub create()
rsize = 100
rd.create(me)
end sub
public sub show()
messagebox.show("room size: " + str(rsize))
messagebox.show("desk size: " + str(rd.getsize()))
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 r as new myroom()
r.show()
end sub
end class
此程序輸出:
room size: 100
desk size: 18
desk之對(duì)象含個(gè)參考 mymother ﹐指向其母對(duì)象。這項(xiàng)關(guān)系是在母子對(duì)象皆建好時(shí)﹐才由create()程序所建立的。于是﹐建立出母子對(duì)象之關(guān)系﹕
綜上所述,當(dāng)myroom類別使用如下指令──
private rd as new desk()
時(shí),才必須把new()與create()分離。如果使用如下指令──
private rd as desk
public sub new()
rd = new desk()
.....
end sub
就不必分離了,原因是:new()與create()的執(zhí)行順序是一致的,例如兩者可合并如下的vb程序:
'ex05.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
class room
protected rsize as double
public function getsize() as double
getsize = rsize
end function
end class
class desk
protected dsize as double
protected mymother as room
public sub new(byval mo as room)
mymother = mo
dsize = mymother.getsize() * 0.18
end sub
public function getsize() as double
getsize = dsize
end function
end class
'----------------------------------------------------
class myroom
inherits room
private rd as desk
public sub new()
rsize = 100
rd = new desk(me)
end sub
public sub show()
messagebox.show("room size: " + str(rsize))
messagebox.show("desk size: " + str(rd.getsize()))
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 r as new myroom()
r.show()
end sub
end class
此程序輸出:
room size: 100
desk size: 18
3. 特類別繼承與母子對(duì)象
您已很熟悉父子類別關(guān)系了﹐這繼承關(guān)系的另一面是母子對(duì)象關(guān)系。例如﹐
'ex06.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
class room
private mother as myroom
public sub new(byval mo as myroom)
mother = mo
end sub
public function getid() as string
getid = mother.yourid()
end function
end class
class myroom
inherits room
public sub new()
mybase.new(me)
end sub
public function yourid() as string
yourid = "vip888"
end function
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 r as new myroom()
messagebox.show(r.getid())
end sub
end class
此程序輸出﹕
vip888
myroom()建構(gòu)者new()將me值給 room的mother參考。于是mother參考到母對(duì)象r。room類別是myroom之父類別﹐但room之對(duì)象卻是myroom之子對(duì)象。
如果將上述room的指令:
public function getid() as string
getid = mother.yourid()
end function
更改為:
public function getid() as string
getid = me.yourid() 'error!!
end function
就錯(cuò)了。因?yàn)閞oom類別里沒有定義yourid()程序。事實(shí)上,vb的繼承機(jī)制里已經(jīng)有母對(duì)象的參考值了,vb的overridable機(jī)制就是基于它而呼叫到子類別(母對(duì)象)的程序的。例如上述程序相當(dāng)于:
'ex07.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
class room
public function getid() as string
getid = me.yourid()
end function
public overridable function yourid() as string
end function
end class
class myroom
inherits room
public sub new()
mybase.new()
end sub
public overrides function yourid() as string
yourid = "vip888"
end function
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 r as new myroom()
messagebox.show(r.getid())
end sub
end class
此程序輸出﹕
vip888
上述ex07.bas比ex06.bas好的地方是:
ex06.bas程序的room類別里面用到myroom的名稱。而ex07.bas程序的room類別里面并沒用到myroom的名稱,因此room類別可以先設(shè)計(jì),myroom類別能后來才設(shè)計(jì),這是繼承機(jī)制的目的之一。不過,如果您一定不想用繼承與overridable概念的話,可使用vb的interface機(jī)制來改善ex06.bas程序,如下:
'ex08.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
interface imyroom
function yourid() as string
end interface
class room
private mother as imyroom
public sub new(byval mo as imyroom)
mother = mo
end sub
public function getid() as string
getid = "roomid: " + mother.yourid()
end function
end class
class myroom
implements imyroom
private base as room
public sub new()
base = new room(me)
end sub
public function yourid() as string implements imyroom.yourid
yourid = "vip888"
end function
public function getid() as string
getid = base.getid()
end function
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 r as new myroom()
messagebox.show(r.getid())
end sub
end class
此程序輸出﹕
roomid: vip888
一般使用委托(delegation)來代替繼承時(shí),常用的手法。然而上述ex08.bas程序的myroom類別里面用到了room名稱,如果您不希望如此,可定義一個(gè)iroom接口,供myroom類別使用,如下程序:
'ex09.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------
interface imyroom
function yourid() as string
end interface
interface iroom
function getid() as string
sub connect(byval m as imyroom)
end interface
class room
implements iroom
private motherobject as imyroom
public function getid() as string implements iroom.getid
getid = motherobject.yourid() + " ***"
end function
public sub connect(byval m as imyroom) implements iroom.connect
motherobject = m
end sub
end class
class myroom
implements imyroom
private base as iroom
public sub connect(byval r as iroom)
base = r
r.connect(me)
end sub
public function yourid() as string implements imyroom.yourid
yourid = "dog888"
end function
public function getid() as string
getid = base.getid()
end function
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 my as new myroom()
dim base as new room()
my.connect(base)
messagebox.show(my.getid())
end sub
end class
此程序輸出﹕
roomid: vip888
room類別里面沒用到myroom名稱,而且myroom類別里沒有用到room名,因此兩個(gè)類別可獨(dú)立設(shè)計(jì)。這是分布式組件軟件,如mts(microsoft transaction server)等系統(tǒng)里的常用手法。n