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

继承|教程|中文

1. 类别之继承

类别之间﹐有些互为独立﹐有些具有密切关系。兹介绍类别间常见的关系──「父子」关系﹔由于儿女常继承父母之生理或心理特征﹐所以又称此关系为「继承」(Inheritance) 关系。类别间之密切关系﹐把相关的类别组织起来﹐并且组织程序内之对象。若程序内之对象毫无组织﹔呈现一片散沙﹐就不是好程序。完美之VB程序﹐必须重视类别间之关系﹐对象是有组织的。
如果 A类别「继承」 B类别﹐则称 A为「子类别」(Subclass)﹐也称B 为「父类别」(Superclass)﹐亦即 B为 A之父类别﹐A 为 B之子类别。在 C++中﹐父类别又称为「基础类别」(Base Class)﹐子类别又称为「衍生类别」(Derived Class) 。也许您觉得「继承」之观念很陌生﹐不知如何看出类别间之继承关系。别担心﹐有个简单方法﹕下列两叙述之意义相同──
(1) A 为 B之子类别。
(2) A 为 B之一种(A kind of) 特殊类别。

根据叙述 (2)能轻易找到父子关系。例如﹕肯尼士(Kennex)生产高品质球拍﹐球拍分两种﹕网球拍与羽球拍。从此句子得知﹕网球拍为一种(A kind of) 球拍﹐羽球拍亦为一种球拍。因之﹐网球拍为球拍之子类别﹐羽球拍亦为球拍之子类别﹐亦即球拍是父类别。以下图标之﹕

图1、 基础类别与衍生类别

如果设计程序来记录球拍之生产情形﹐则程序应定义基础类别──球拍﹐以及两衍生类别──网球拍及羽球拍。程序应藉继承关系将三类别组织起来。除了物品(如球拍、汽车等)外﹐人也有继承关系。例如﹕学校人员包括学生、老师及职员﹐老师又分为专任老师及兼任老师。学校的人事软件系统﹐应定义类别关系如下﹕

图2、 三代继承关系

程序不仅要定义类别﹐也要定义其继承关系。

2. 定义继承关系

前面各章里﹐已介绍如何定义类别﹔本节将告诉您如何定义类
别之继承关系。兹举例说明之﹕

程序的设计过程是﹕
step 1. 定义基础类别(父类别)。如﹕

Class Person
‥‥
End Class

step 2. 定义衍生类别(子类别)。如﹕

Class Teacher
Inherits Person
‥‥
End Class
Class Student
Inherits Person
‥‥
End Class

Inherits字眼之后为父类别名称。它表达了:Teacher 为Person之子类别﹐且 Student为Person之子类别。现在﹐已知道如何表达继承关系了﹔然而﹐子类别从父类别继承什么东西呢﹖类别包含「资料」及「程序」。因之﹐子类别继承父类别之资料及程序。现在﹐请看看如何继承资料及程序。下述程序定义 Person 类别﹐含有 2项资料及 3个程序﹕

'ex01.bas
Imports System.ComponentModel
Imports System.Drawing
Imports System.WinForms
'-----------------------------------------------------------------------------------------
Class Person
Private name As String
Private age As Integer
Public Sub SetValue(ByVal na As String, ByVal a As Integer)
name = na
age = a
End Sub
Public Function birthYear() As Integer
birthYear = 2001 - age
End Function
Public Sub Display()
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 mike As Person
mike = New Person()
mike.SetValue("Mike", 45)
mike.Display()
Messagebox.Show("BirthYear: " + Str(mike.birthYear()))
End Sub
End Class

此程序输出如下﹕
Name: Mike Age: 45
BirthYear: 1956

所谓继承资料﹐表示继承资料成员之定义﹐而不是继承资料之值﹐请详加区别之。类别定义资料成员(含型态及名称)﹐对象诞生后﹐对象内才有资料值。所以「类别继承」乃继承类别之定义﹐不是继承对象之值。也就是说﹕若父类别定义name及 age两个资料成员﹐则子类别天生就拥有此两个资料成员﹐所以子类别不需定义它们。所谓继承程序﹐表示子类别天生就拥有父类别定义之程序成员。例如﹕Person的子类别天生就具有 SetValue()、birthYear()及Display()程序。现在﹐就来定义Person之子类别。

'ex02.bas
Imports System.ComponentModel
Imports System.Drawing
Imports System.WinForms
'-----------------------------------------------------------------------------------------
Public Class Person
Private name As String
Private age As Integer
Public Sub SetValue(ByVal na As String, ByVal a As Integer)
name = na
age = a
End Sub
Public Function birthYear() As Integer
birthYear = 2001 - age
End Function
Public Sub Display()
Messagebox.Show("Name: " + name + " Age: " + str(age))
End Sub
End Class

Public Class Teacher
Inherits Person

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 steven As Teacher
steven = New Teacher()
steven.SetValue("Steven", 35)
steven.Display()
End Sub
End Class

此程序输出如下﹕
Name: Steven Age: 35

表面上Teacher 类别中﹐未定义数据项及程序﹐但事实上已拥有name及 age两个资料成员﹐也拥有 SetValue()、birthYear()及Display() 三个程序成员。因之﹐steven为Teacher类别之对象﹐它能呼叫SetValue()及Display()程序。在应用上﹐子类别通常拥有自己的数据项及程序﹐使其有别于父类别。例如﹕

'ex03.bas
Imports System.ComponentModel
Imports System.Drawing
Imports System.WinForms
'------------------------------------------------------------------------------------------------------
Public Class Person
Private name As String
Private age As Integer
Public Sub SetValue(ByVal na As String, ByVal a As Integer)
name = na
age = a
End Sub
Public Function birthYear() As Integer
birthYear = 2001 - age
End Function
Public Sub Display()
Messagebox.Show("Name: " + name + " Age: " + str(age))
End Sub
End Class

Public Class Teacher
Inherits Person

Private salary As Decimal
Public Sub tr_SetValue( ByVal na As String, ByVal a As Integer, ByVal
sa As Decimal)
SetValue(na, a)
salary = sa
End Sub
Public Sub pr()
MyBase.Display()
Messagebox.Show("Salary: " + str(salary))
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 steven As Teacher
steven = New Teacher()
steven.tr_SetValue("Steven", 35, 35000)
steven.pr()
End Sub
End Class

此程序输出如下﹕
Name: Steven Age: 35
Salary: 35000

现在﹐Teacher 类别含有三个资料成员﹕
1. name ──从Person类别继承而来。
2. age ──从Person类别继承而来。
3. salary ──自己定义的。

此外﹐也含有五个程序成员﹕
1. SetValue() ──从Person继承而来。
2. birthYear()──从Person继承而来。
3. Display() ──从Person继承而来。
4. tr_SetValue()──自己定义的。
5. pr()──自己定义的。

由于SetValue()为 Teacher之程序成员﹐所以tr_SetValue()能直接呼叫SetValue()来设定name及age值﹔之后﹐tr_SetValue()自己设定salary之值。同理﹐pr()能直接呼叫 Display()来显示name及age之内容﹔之后﹐pr()自己输出salary之值。也许﹐您会问道﹕子类别自己定义之程序﹐是否能与父类别之程序同名称呢﹖答案是肯定的﹐而且是常见之写法。如果名称相同但是参数不一致(像参数个数不同或参数型态不同),就是父子类别之间的程序多重定义(Procedure Overloading)了,必须写上Overloads字眼。例如﹕上述程序相当于──

'ex04.bas
Imports System.ComponentModel
Imports System.Drawing
Imports System.WinForms
'------------------------------------------------------------------------------------------
Class Person
Private name As String
Private age As Integer
Public Sub New()
End Sub
Public Sub SetValue(ByVal na As String, ByVal a As Integer)
name = na
age = a
End Sub
Public Function birthDay() As Integer
birthDay = 2001 - age
End Function
Public Sub Display()
Messagebox.Show("Name: " + name + " Age: " + str(age))
End Sub
End Class

Class Teacher
Inherits Person

Private salary As Decimal
Public Overloads Sub SetValue( ByVal na As String, ByVal a As Integer, ByVal
sa As Decimal)
SetValue(na, a)
salary = sa
End Sub
Public Sub pr()
MyBase.Display()
Messagebox.Show("Salary: " + str(salary))
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 steven As Teacher
steven = New Teacher()
steven.SetValue("Steven", 35, 35000)
steven.pr()
End Sub
End Class

此程序输出如下﹕
NAME: Steven
AGE : 35
Salary: 35000

此种情况﹐Teacher 类别拥有两个 SetValue()程序﹐一个由Person继承而来﹐一个是自己定义的。同时﹐也拥有两个Display()程序。此时﹐计算机如何分辨它们呢﹖计算机依据程序的参数来决定呼叫那一个程序,当您写成 steven.SetValue("Steven", 35, 35000)时,此SetValue()是子类别自己的SetValue()。而写成SetValue(na,a)时,则是由Person类别继承而来之SetValue()程序。此VB程序已定义如下之类别关系﹕

接下来﹐再为Person定义一个子类别── Student。程序如下﹕

'ex05.bas
Imports System.ComponentModel
Imports System.Drawing
Imports System.WinForms
'------------------------------------------------------------------------------------------
Public Class Person
Private name As String
Private age As Integer
Public Sub New()
End Sub
Public Sub SetValue(ByVal na As String, ByVal a As Integer)
name = na
age = a
End Sub
Public Function birthDay() As Integer
birthDay = 2001 - age
End Function
Public Sub Display()
Messagebox.Show("Name: " + name + " Age: " + str(age))
End Sub
End Class

Public Class Teacher
Inherits Person

Private salary As Decimal
Public Overloads Sub SetValue( ByVal na As String, ByVal a As Integer, ByVal
sa As Decimal)
SetValue(na, a)
salary = sa
End Sub
Public Sub pr()
MyBase.Display()
Messagebox.Show("Salary: " + str(salary))
End Sub
End Class

Public Class Student
Inherits Person

Private student_number As Integer
Public Overloads Sub SetValue( ByVal na As String, ByVal a As Integer, ByVal
no As Integer)
SetValue(na, a)
student_number = no
End Sub
Public Sub pr()
MyBase.Display()
Messagebox.Show("StudNo: " + str(student_number))
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 x As New Person()
x.SetValue("Alvin", 32)
Dim y As New Student()
y.SetValue("Tom", 36, 11138)
x.Display()
y.pr()
End Sub
End Class

此程序输出﹕
Name: Alvin Age: 32
Name: Tom Age: 36
StudNo: 11138

此时﹐Student 类别含有name、age 及student_number三个资料成员。而且拥有 SetValue()、Person.SetValue() 、Display()、pr() 及birthYear()五个程序成员。于是建立了如下之继承关系﹕

x 对象含name及age 两项资料﹐指令──x.SetValue ("Alvin", 32)﹐将资料存入x 对象中。因Student继承Person﹐所以y 对象内含name、age 及student_number三项资料﹐指令──y.SetValue("Tom", 36, 11138) 将资料存入y 对象中。上述 SetValue()程序之功能是﹕设定对象之初值。可由建构者(Constructor) 代替之。所以此Student之定义相当于──

'ex06.bas
Imports System.ComponentModel
Imports System.Drawing
Imports System.WinForms

'----------------------------------------------------
Public Class Person
Private name As String
Private age As Integer
Public Sub New()
End Sub
Public Sub SetValue(ByVal na As String, ByVal a As Integer)
name = na
age = a
End Sub
Public Function birthDay() As Integer
birthDay = 2001 - age
End Function
Public Sub Display()
Messagebox.Show("Name: " + name + " Age: " + str(age))
End Sub
End Class

Public Class Teacher
Inherits Person

Private salary As Decimal
Public Overloads Sub SetValue(ByVal na As String, ByVal a As Integer, ByVal sa As Decimal)
SetValue(na, a)
salary = sa
End Sub
Public Sub pr()
MyBase.Display()
Messagebox.Show("Salary: " + str(salary))
End Sub
End Class

Public Class Student
Inherits Person

Private student_number As Integer
Public Sub New(ByVal na As String, ByVal a As Integer, ByVal no As Integer)
SetValue(na, a)
student_number = no
End Sub
Public Sub pr()
MyBase.Display()
Messagebox.Show("StudNo: " + str(student_number))
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 x As New Person()
x.SetValue("Alvin", 32)
Dim y As New Student("Tom", 36, 11138)
x.Display()
y.pr()
End Sub
End Class

此程序输出﹕
Name: Alvin Age: 32
Name: Tom Age: 36
StudNo: 11138

子类别之建构者呼叫父类别之SetValue()来设定对象初值。请仔细看y 对象之诞生过程﹕y 诞生时﹐建构者──

Public Sub New(ByVal na As String, ByVal a As Integer, ByVal no As Integer)
SetValue(na, a)
student_number = no
End Sub

它先呼叫父类别的自然建构者Person.New()一起诞生y 对象。此时﹐Person.New()诞生如下﹕

继承部分是Person.New()诞生的。Person.New()任务完成了﹐轮到Student的建构者本身诞生(扩充)如下﹕

诞生完毕﹐开始执行Student建构者内之指令以设定y 之初值。首先呼叫父类别的SetValue() ﹐于是SetValue()将值存入y 如下﹕

最后﹐执行指令── student_number = no﹐设定student_number数据之初值﹕

于是﹐Student的建构者诞生y 对象﹐也设定初值﹐功成圆满。
上述的Student建构者呼叫父类别之自然建构者(因为Person类别未定义建构者﹐计算机自动产生自然建构者)来协助诞生对象。如果Person类别定义了建构者﹐Student建构者便能直接呼叫Person建构者了。例如﹕

'ex07.bas
Imports System.ComponentModel
Imports System.Drawing
Imports System.WinForms
'-----------------------------------------------------------------------------------------
Public Class Person
Private name As String
Private age As Integer
Public Sub New(ByVal na As String, ByVal a As Integer)
name = na
age = a
End Sub
Public Function birthDay() As Integer
birthDay = 2001 - age
End Function
Public Sub Display()
Messagebox.Show("Name: " + name + " Age: " + str(age))
End Sub
End Class

Public Class Teacher
Inherits Person

Private salary As Decimal
Public Sub New(ByVal na As String, ByVal a As Integer, ByVal sa As Decimal)
MyBase.New(na, a)
salary = sa
End Sub
Public Sub pr()
MyBase.Display()
Messagebox.Show("Salary: " + str(salary))
End Sub
End Class

Public Class Student
Inherits Person

Private student_number As Integer
Public Sub New(ByVal na As String, ByVal a As Integer, ByVal no As Integer)
MyBase.New(na, a)
student_number = no
End Sub
Public Sub pr()
MyBase.Display()
Messagebox.Show("StudNo: " + str(student_number))
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 x As New Person("Alvin", 32)
Dim y As New Student("Tom", 36, 11138)
x.Display()
y.pr()
End Sub
End Class

此程序输出﹕
Name: Alvin Age: 32
Name: Tom Age: 36
StudNo: 11138

子类别之建构者──

Public Sub New(ByVal na As String, ByVal a As Integer, ByVal sa As Decimal)
......
End Sub

先呼叫父类别之建构者──

Public Sub New(ByVal na As String, ByVal a As Integer)
......
End Sub

协助诞生对象如下﹕

接着﹐执行父类别Person的建构者内之指令﹐设定初值如下﹕

Person建构者工作完成了﹐轮到Student建构者的工作﹐扩充对象如下﹕

诞生完毕﹐执行Student建构者内之指令﹐设定初值﹕

于是﹐y 对象诞生完毕了。
子类别建构者呼叫父类别建构者之模式﹕

例如﹐Student建构者内的MyBase.New(na, a)指令将na及a值传递给Person建构者﹐于是Person建构者设定name及age之初值。除了呼叫Person建构者之外﹐还执行自己的指令──student_number = no ﹐设定student_number之初值。Student类别之对象含有三个资料成员﹐其中name及 age是由Person类别继承而来﹐就藉Person建构者设定其初值。至于Student类别自己定义之资料成员student_number﹐就由自己的指令──student_number = no设定其初值。
3. 藉「继承」扩充程序

程序之发展是渐进的﹐软件内之类别需随企业之成长而不断扩充。类别扩充常源于﹕
(1) 企业成长了﹐产生了新类别。
例如﹕某计算机公司﹐过去制造大型及迷你计算机﹐现在新推出个人计算机。就必须建立新子类别如下﹕

图3、 程序扩充方法──衍生子类别

再如﹐某银行过去提供支票帐户(Checking Account)及储蓄帐户(Saving Account)给客户﹐现在拟增加定期帐户(CD Account)。就得新增子类别如下﹕

这新类别CD_Account继承父类别Saving_Account及Account 的资料及程序。例如﹐Account 之定义如下﹕

'ex08.bas
Imports System.ComponentModel
Imports System.Drawing
Imports System.WinForms
'-----------------------------------------------------------------------------------
Class Account
Private Shared next_number As Integer
Protected acc_no As Integer
Protected name As String
Protected balance As Decimal

Shared Sub New()
next_number = 1000
End Sub
Public Sub New(ByVal na As String)
name = na
balance = 0
acc_no = next_number
next_number = next_number + 1
End Sub
Public Sub deposit(ByVal money As Decimal)
balance = balance + money
End Sub
Public Sub withdraw(ByVal money As Decimal)
balance = balance - money
End Sub
Public Sub close()
acc_no = -1
End Sub
End Class

Class Saving_Account
Inherits Account

Private interest_rate As Double
Public Sub New(ByVal na As String, ByVal rate As Double)
MyBase.New(na)
Me.interest_rate = rate
End Sub
Public Function comp_interest() As Double
comp_interest = balance * interest_rate
End Function
Public Sub display()
Messagebox.Show("ACC_NO: " + str(MyBase.acc_no))
Messagebox.Show("余额: " + str(Me.balance) + " 利息: "
+ str(comp_interest()))
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 saving_acc As New Saving_Account("Tom Bush", 0.085)
Dim account As Saving_Account
account = New Saving_Account("Linda Lin", 0.008)
account.deposit(5000)
account.withdraw(2500)
account.display()
account.close()
End Sub
End Class

此程序输出﹕
ACC_NO: 1001
余额: 2500
利息: 20
因Saving_Account继承Account ﹐所以Saving_Account类别亦拥有deposit() 、withdraw()及close() 三个程序。请换个角度﹐从Saving_Account类别之设计者来看﹐他透过继承关系来「借用」父类别之程序。「借用」又称为「再使用」(Resue) ﹐能使得子类别简单但却强而有力。就如Saving_Account类别显得比Account 类别简单﹐但它总共却含有deposit() 、withdraw()等6 个程序﹐功能比Account 类别强大许多。对于指令──account.deposit(5000) 而言﹐其有两种意义﹕
I. Saving_Account继承Account ﹐所以Saving_Account具有deposit() 程序﹐可将5000存入account 对象。
II. Saving_Account为Account 之子类别﹐它能借用(再使用)父类别之deposit() 程序﹐来将5000存入account 对象。

这两者是一体的两面﹐继承功能让软件师能不断新增子类别﹐不断再使用现有类别之程序﹐来简化子类别之复杂度。继承使得子类别拥有祖父类别之各种功能﹐且再增加新功能﹔再使用使得子类别尽量委托祖父类别来担任工作﹐自己以逸待劳。因而﹐善用继承功能﹐时时心存再使用观念﹐便能设计简单却功能强大的子类别了。而程序员的效率提升了﹐软件之成本下降了﹐此乃OOP 的理想目标之一。兹再扩充一个子类别CD_Account如下﹕

'ex09.bas
Imports System.ComponentModel
Imports System.Drawing
Imports System.WinForms
'--------------------------------------------------------------------------------
Class Account
Private Shared next_number As Integer

Protected acc_no As Integer
Protected name As String
Protected balance As Decimal

Shared Sub New()
next_number = 1000
End Sub
Public Sub New(ByVal na As String)
name = na
balance = 0
acc_no = next_number
next_number = next_number + 1
End Sub
Public Sub deposit(ByVal money As Decimal)
balance = balance + money
End Sub
Public Sub withdraw(ByVal money As Decimal)
balance = balance - money
End Sub
Public Sub close()
acc_no = -1
End Sub
End Class

Class Saving_Account
Inherits Account

Private interest_rate As Double
Public Sub New(ByVal na As String, ByVal rate As Double)
MyBase.New(na)
Me.interest_rate = rate
End Sub
Public Function comp_interest() As Double
comp_interest = balance * interest_rate
End Function
Public Sub display()
Messagebox.Show("ACC_NO: " + str(MyBase.acc_no))
Messagebox.Show("余额: " + str(Me.balance) + " 利息: "
+ str(comp_interest()))
End Sub
End Class

Class CD_Account
Inherits Saving_Account

Public Sub New(ByVal na As String, ByVal rate As Double, ByRef b As Decimal)
MyBase.New( na, rate + 0.003 )
MyBase.deposit(b)
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 a As CD_Account
a = New CD_Account("Linda Lin", 0.008, 2500)
a.display()
a.close()
End Sub
End Class

CD_Account除了在定义自己的New()之外,皆继承Saving_Account的成员。请您仔细阅读下述程序,看看跟上一个程序有何微妙差异?

'ex10.bas
Imports System.ComponentModel
Imports System.Drawing
Imports System.WinForms
'--------------------------------------------------------------------------------
Class Account
Private Shared next_number As Integer

Protected acc_no As Integer
Protected name As String
Protected balance As Decimal

Shared Sub New()
next_number = 1000
End Sub
Public Sub New(ByVal na As String)
name = na
balance = 0
acc_no = next_number
next_number = next_number + 1
End Sub
Public Sub deposit(ByVal money As Decimal)
balance = balance + money
End Sub
Public Sub withdraw(ByVal money As Decimal)
balance = balance - money
End Sub
Public Sub close()
acc_no = -1
End Sub
End Class

Class Saving_Account
Inherits Account

Private interest_rate As Double
Public Sub New(ByVal na As String, ByVal rate As Double)
MyBase.New(na)
Me.interest_rate = rate
End Sub
Public Function comp_interest() As Double
comp_interest = balance * interest_rate
End Function
Public Sub display()
Messagebox.Show("ACC_NO: " + str(MyBase.acc_no))
Messagebox.Show("余额: " + str(Me.balance) + " 利息: "
+ str(comp_interest()))
End Sub
End Class

Class CD_Account
Private acc As Saving_Account
Public Sub New(ByVal na As String, ByVal rate As Double, ByRef b As Decimal)
acc = New Saving_Account(na, rate + 0.003)
acc.deposit(b)
End Sub
Public Function comp_interest() As Double
comp_interest = acc.comp_interest()
End Function
Public Sub display()
acc.display()
End Sub
Public Sub Close()
acc.close()
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 a As CD_Account
a = New CD_Account("Linda Lin", 0.008, 2500)
'a.deposit(5000) 'Error here!!
a.display()
a.close()
End Sub
End Class

(2) 环境改变了﹐必须修正旧类别。
例如﹕软件内含Sales 类别如下──

'ex11.bas
Imports System.ComponentModel
Imports System.Drawing
Imports System.WinForms
'----------------------------------------------------------------------------------
Class Sales
Protected sum As Double
Public Sub New()
sum = 0
End Sub
Public Overridable Sub input(ByVal amount As Double)
sum = sum + amount
End Sub
Public Sub display()
Messagebox.Show("The Sum Is: " + str(sum))
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 x As New Sales()
x.input(3000.5)
x.input(7999.5)
x.input(15000)
x.display()
End Sub
End Class

此程序输出如下﹕The Sum Is: 26000

input() 输入销售金额﹐且将金额加总起来﹐存于 sum变量中。display() 显示出总金额。随着企业之成长﹐往往对软件之功能有新的需求。例如﹕希望程序输出平均销售额。此时﹐必须更新原来的软件﹐其方法有二﹕

(I) 改写 Sales类别之定义。
此为传统软件之维护方法﹐就像一座房屋﹐已不敷使用﹐于是用推土机推掉﹐重建之。通常这种方法并不佳﹐原因是﹕旧的软件有些功能仍然可用﹐废弃重建实在可惜。若不想重建﹐只想去修改它﹐但是软件若很庞大﹐得浪费人力去了解旧软件的结构﹐弄通了之后﹐才能加以修正﹐显然不经济。进一步想﹐于修正之过程中﹐不经意而更动旧软件内仍可用之部分﹐则原来正确之功能﹐反而有遭破坏之虞﹗
(II) 藉继承关系修正 Sales类别。
亦即定义 Sales之子类别﹐来修正及扩充 Sales之功能。此时﹐并未更动Sales 类别之定义﹐所以不会破坏 Sales之功能。既然保存原有的良好功能﹐又能增加新的功能﹐岂非两全其美呢﹖此「旧干发新枝」之景象即是千年神木得以茁壮且绿意盎然之道理﹐也正是 OOP魅力的源头。现在﹐就为 Sales定义个子类别叫 Sales_new如下﹕

'ex12.bas
Imports System.ComponentModel
Imports System.Drawing
Imports System.WinForms
'------------------------------------------------------------------------------------
Class Sales
Protected sum As Double
Public Sub New()
sum = 0
End Sub
Public Overridable Sub input(ByVal amount As Double)
sum = sum + amount
End Sub
Public Sub display()
Messagebox.Show("The Sum Is: " + str(sum))
End Sub
End Class

Class Sales_new
Inherits Sales

Protected number As Integer
Public Sub New()
number = 0
End Sub
Public Overrides Sub input(ByVal amount As Double)
MyBase.input(amount)
number = number + 1
End Sub
Public Sub display_average()
Dim avg As Double
If number > 0 Then
avg = sum / number
MessageBox.Show("The Average Is : " + str(avg))
End If
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 x As New Sales_new()
x.input(3005.5)
x.input(5994.5)
x.input(15012.6)
x.display()
x.display_average()
End Sub
End Class

此程序输出﹕
The Sum Is: 24012.6
The Average Is: 8004.2

子类别增加了新数据项number﹐重新定义了input()﹐使新input()保存父类别内之input()功能﹐并加上number = number +1之运算。保留了父类别之display()﹐但增加了display_average()程序。依此方法﹐软件自然日益茁壮﹐且历久弥新﹗藉继承关系不断扩充乃为OOP之重要技术﹐盼您善用之。n

时间: 2024-11-05 14:56:57

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

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

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

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中文教程(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中文教程(5)程序多重定义

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

VB.Net中文教程(6) 母子对象关系

对象|教程|中文 1. 特殊Whole-Part关系 ---- 母子对象关系 大家已经熟悉父子类别关系﹐也就是「继承」关系了.于此说明另一种常见关系── 母子对象.一般称之为「组合/部分」关系.日常生活中﹐处处可见到这种母子对象.例如﹐客厅内有沙发.桌子.椅子等等.客厅是母对象﹐沙发.桌子.椅子是子对象.再如计算机屏幕上的窗口内有按钮.选择表.对话盒等等.窗口是母对象﹐按钮.选择表是子对象.于此将说明如何建立母子对象关系.有了关系﹐母子就能互相沟通了.母子对象之间﹐如何沟通呢﹖也就是说﹐母对象如

VB.Net中文教程(9) 重新定义(Overriding)程序

程序|教程|中文 主题: 重新定义(Overriding)程序 ?????????? 内容 ??????????v 1. 重新定义程序 1. 重新定义(Override)程序 在应用上﹐常见如下之情况﹕子类别从父类别继承之程序﹐并不合乎子类别之需要.此时可设计新程序取代之. 图1.程序成员之重新定义 例如﹐SalesPerson类别含有Bonus()程序﹔SalesManager由 SalesPerson继承而得Bonus()程序.Bonus()能计算销售人员之红利.然而﹐一般销售员与销售经理之

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

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

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"

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

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