1. 继承与封装性(encapsulation)
1.1 公用与私有数据
前面已介绍「封藏性」(encapsulation) 之观念。即是﹕类别内所定义之资料成员﹐只限于程序成员才能存取之。现在所面临之问题为﹕子类别能否直接存取父类别之资料呢﹖就如同﹕儿女从父母亲继承了财产﹐但能否取用或卖掉继承而来的财产呢﹖如果可以﹐显然违背了「封藏性」之理想。如果不行﹐显然带给程序员莫大之限制。理论上﹐百分之百的封藏性最为完美﹔但应用上﹐若给予子类别若干优待﹐能提高程序之弹性及效率。为了解决此鱼与熊掌不可兼得之困境﹐vb提供三种选择﹕
(1) public ──指定某些数据为公用的﹐任何程序皆可直接取用之﹔此时并无任何封藏作用。
(2) protected ──指定某些资料为家族公用﹐亦即只有子孙类别内之程序可取用﹐非子孙类别之程序必须呼叫家族内之程序代为存取。
(3) private ──指定某资料为类别私有﹐只限于该类别之程序才可取用。子类别之程序也必须呼叫父类别之程序代为存取﹐此时具百分之百封藏性。
先前介绍「封装性」基本概念时,您已经认识了public和private的用意了,至于protected则配合继承来使用,于是在此特别强调它。
由于vb向现实妥协﹐开了方便之门﹐无百分之百封藏性﹔所以有些人认为 vb并非完美的 oop语言。您认为如何呢﹖请看个程序﹕
'ex01.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'---------------------------------------------------------
class person
private name as string
public age as integer
public sub new(byval na as string, byval a as integer)
name = na
age = a
end sub
end class
class teacher
inherits person
public salary as single
public sub new(byval na as string, byval a as integer, byval sa as single)
mybase.new(na, a)
salary = sa
end sub
public sub modifyage(byval a as integer)
age = a
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 crag as new teacher("crag", 38, 45000)
crag.modifyage(42)
messagebox.show( "age: " + str(crag.age) + ", salary: "
+ str(crag.salary))
end sub
end class
此程序输出如下﹕
age: 42 salary: 45000
person之age资料成员定义为 public﹐表示 age为公用数据﹐任何程序皆可存取之。因之﹐teacher 之 modifyage()可使用age这名称。相对地﹐于 teacher类别中﹐就不得使用name这名称﹐因为name定义为privatec﹐表示name为person类别之私有资料。再看teacher类别之salary资料﹐它定义为public﹐表示公用之意。所以form1_click()可直接使用 crag.salary格式取得salary之值。然而﹐不能写成﹕crag.name ﹐因为name为私有资料。例如,下述程序是不对的:
'ex02.bas
'some error here!
imports system.componentmodel
imports system.drawing
imports system.winforms
'---------------------------------------------------------
class person
private name as string
public age as integer
public sub new(byval na as string, byval a as integer)
name = na
age = a
end sub
end class
class teacher
inherits person
public salary as single
public sub new(byval na as string, byval a as integer, byval sa as single)
mybase.new(na, a)
salary = sa
end sub
public sub modifyname(byval na as string)
name = na 'error here!
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 crag as new teacher("crag", 38, 45000)
crag.modifyname("crag clinton")
messagebox.show( "age: " + str(crag.age) + ", salary: "
+ str(crag.salary))
end sub
end class
因为name是person类别的私有资料,在子类别teacher的modifyname()里不能使用此资料名称,所以错了。如果将person类别定义为﹕
'ex03.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'---------------------------------------------------------
class person
protected name as string
public age as integer
public sub new(byval na as string, byval a as integer)
name = na
age = a
end sub
public function getname() as string
getname = name
end function
end class
class teacher
inherits person
public salary as single
public sub new(byval na as string, byval a as integer, byval sa as single)
mybase.new(na, a)
salary = sa
end sub
public sub modifyname(byval na as string)
name = na
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 crag as new teacher("crag", 38, 45000)
crag.modifyname("crag clinton")
messagebox.show( "age: " + str(crag.age) + ", name: "
+ crag.getname())
end sub
end class
此程序输出:
age: 42, name: crag clinton
此时﹐name为家族公用之资料﹐凡是person之子孙类别皆可取用之。但家族外之程序(如 form1_click()程序)仍不得直接使用之。如果上述定义改为﹕
class person
protected name as string
private salary as decimal
public age as integer
public sub new(byval na as string, byval a as integer)
name = na
age = a
end sub
end class
此时﹐salary为类别私有﹐其它类别不得使用。name为家族私有﹐家族外之类别不得使用。age为公用﹐任何类别皆可用。
1.2 公用与私有程序
上节介绍过﹕资料成员有 private、protected 及public之分。同样地﹐程序成员也可分为 private、protected 及public。虽然在应用上﹐程序成员大多定义为public﹐但必要时﹐也能将过程定义为private 或 protected。私有程序和私有资料一样﹐只限于该类别之程序才能呼叫它。例如﹕
'ex04.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------------------------
class person
private name as string
private age as integer
private sub modifyage(byval a as integer)
age = a
end sub
public sub new(byval na as string, byval a as integer)
name = na
age = a
end sub
public sub modify(byval na as string, byval a as integer)
name = na
modifyage(a)
end sub
public sub show()
messagebox.show("name: " + name + " age: " + str(age))
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 p1 as new person("david", 25)
p1.show()
p1.modify("david smith", 28)
p1.show()
end sub
end class
此程序输出:
name: david age: 25
name: david smith age: 45
modifyage()为私有程序﹐new()及modify()为公用程序。modifyage()为person类别之程序成员﹐所以modify()能呼叫modifyage()。person类别外之函数不能呼叫modifyage()。例如﹕在form1_click()中﹐可写着 p1.modify()﹐因为modify()为公用程序。但于form1_click()内﹐就不可写着 p1.modifyage()﹐因为modifyage()为 private函数。如果放宽对modifyage()之限制﹐使person之子类别能呼叫modifyage()﹐就须定义modifyage()为 protected函数﹐如下﹕
'ex05.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------------------------
class person
protected name as string
private age as integer
protected sub modifyage(byval a as integer)
age = a
end sub
public sub new(byval na as string, byval a as integer)
name = na
age = a
end sub
public sub show()
messagebox.show("name: " + name + " age: " + str(age))
end sub
end class
class teacher
inherits person
public sub new(byval na as string, byval a as integer)
mybase.new(na, a)
end sub
public sub modify(byval na as string, byval a as integer)
mybase.name = na
mybase.modifyage(a)
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 p1 as new teacher("david", 25)
p1.show()
p1.modify("kent smith", 45)
p1.show()
end sub
end class
此程序输出:
name: david age: 25
name: kent smith age: 45
此时﹐子孙类别之函数能呼叫modifyage()﹐但家族外之类别仍不可呼叫它。总结归纳为简单规则﹕
◎如果允许任何类别使用﹐就宣告为public。
◎如果允许子类别使用﹐就宣告为protected 。
◎如果允许本类别使用﹐就宣告为private 。
请在看个例子:
'ex06.bas
imports system.componentmodel
imports system.drawing
imports system.winforms
'----------------------------------------------------------------------
class person
protected name as string
private age as integer
protected sub modifyage(byval a as integer)
age = a
end sub
public sub new(byval na as string, byval a as integer)
name = na
age = a
end sub
public sub show()
messagebox.show("name: " + name + " age: " + str(age))
end sub
end class
class teacher
inherits person
public sub new(byval na as string, byval a as integer)
mybase.new(na, a)
end sub
protected sub modify(byval na as string, byval a as integer)
mybase.name = na
mybase.modifyage(a)
end sub
end class
class fulltime_teacher
inherits teacher
public sub new(byval na as string, byval a as integer)
mybase.new(na, a)
end sub
public sub modifyvalue(byval na as string, byval a as integer)
mybase.modify(na, a)
end sub
public sub modifydata(byval na as string, byval a as integer)
mybase.name = na
mybase.modifyage(a)
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 p1 as new fulltime_teacher("david", 25)
p1.show()
p1.modifyvalue("kent smith", 45)
p1.show()
end sub
end class
此程序输出:
name: david age: 25
name: kent smith age: 45
show()是person类别的public程序,孙子类别fulltime_teacher继承之,成为fulltime_teacher类别的public程序,所以form1_click()程序能写着指令:p1.show()。name和modifyage()是person的protected成员,teacher的modify()程序里能直接使用它们。而且fulltime_teacher的modifydata()程序里能直接使用它们。但是form1_click()就无法使用它们。modify()是teacher的protected成员,fulltime_teacher的modifyvalue()程序里能直接使用它们,但form1_click()就不行使用。所以若将上述form1_click()的指令改为如下,就不对了:
protected sub form1_click( ..... )
dim p1 as new fulltime_teacher("david", 25)
p1.show()
p1.modify("kent smith", 45) 'error!
p1.modifyage(24) 'error!
p1.show()
end subend class
n