-
vb.net教程之什么时候使用继承
什么时候使用继承
继承是一个非常有用的概念,但是却很容易被用得不合适,通常用接口来实现可能会更好,本文的目的就是使用户懂得怎样更好的使用类的继承。
当遇到如下情况时,继承将是一个好的选择:
(1)简化一个低等级的不使用类的API函数
(2)从基本类中得到重用的代码
(3)需要对不同的数据类型使用相同名称的类和方法
(4)类的层次相当,最多4到5级,而且不仅增加一级或两级
(5)通过改变一个基本类,就改变所有的派生类。
1.简化一个低等级的不使用类的API函数
类库使得一系列的函数的调用更加合理,特别是当调用的这些函数不是能类的形式组织的时候,举一个例子,就象那些为简化在窗体中画图表的API函数而设计的类一样,这种类库在有些时候被叫做"Wrapper"。这些把API函数包括起来的类库通常可以简化操作,不过这样做并没有企图把API函数的一些基本特性给隐藏掉。Windows Graphics API函数有很多,但用户并不需要了解它底层的一些变化。用户在设计类的时候也是这样,必须考虑类库的用户对底层的了解程序。实际上,很多类库都是根据API函数来组织的,通常它们有两个趋势:使得API函数对高级用户更加方便,或者通过高度的概括,把这些底层的信息隐含起来,这意味着把Windows API函数的具体信息对用户封装。
但是这里又有一个隐含的关于开发类库的问题,被打成包的类导致了非常多的文字说明,把API简化得越多,需要的文字说明就越多。经常性的,几百个API函数被组织成一打或一百个类,每一个类具有几十个或几百个重叠的属性、方法和事件,整理或者浏览这个类将是工作量非常大的一件事。
设计和维护一个把各种API打包的类库,是一件工作量非常大的事情,设想现在要写一个类库来简化一个为Web服务的Html的网页的生成过程,这就需要先考虑一下自己是不是需要知道"Html";是不是需要把这些诸如"tags"或"angle brackets"的概念隐含掉;是不是向用户显示一些诸如字体等这些属性;是不是只要能处理Html就行了,还是需要处理XMl,能不能叫XHtml?等等。
假设一个基本类含有一个MustOverride方法叫做ParseHtml,然而当需要支持XMl时,就必须把Html支持替换掉,此时需要把方法ParseHtml清空,惟一的选择是建立一个ParseXMl方法,但不能把ParseHtml方法移除掉,因为当把该方法移除以后,就会导致Html业务的丧失,因为整个类是分布的,用户必须让ParseHtml和ParseXml方法同时存在,但这让内存和运行时间效率都减低了。
诸如这种问题在网络开发环境还是存在的,用户可能是过早设计了Html类库,也许一个基于接口的系统可以更好的解决这个问题。它让用户建立一系列的Html接口,当在确认XHtml或者XML是更好的基底时,就可以放弃或改善Html接口。
2.从基本类中得到重用的代码
使用继承的一个重要的原因就是为了代码的重用,然而这也是最危险的。正是因为代码的重用,即使是设计的最好的系统,有的时候也会出现一些设计者难以预料的改变。
一个经典的表示代码重用的高效率的例子就是一个数据管理库,设想现在有一个大型的应用程序,用来管理几个在内存中的清单,另一个是从顾客的数据中拷贝来的清单,该数据结构可以如下所示:
Class CustomerInfo
Public PreviousCustomer As CustomerInfo
Public NextCustomer As CustomerInfo
Public ID As Integer
Public FullName As String
'Adds a CustomerInfo to list
Function Insertcustomer As CustomerInfo
...
End Function
'Removes a CustomerInfo from list
Function DeleteCustomer As CustomerInfo
...
End Function
'Obtains next CustomerInfo in list
Function GetNextCustmer As CustomerInfo
...
End Function
'Obtains previous CustomerInfo
Function GetPreCustomer As CustomerInfo
...
End Function
End Class
可能还有一张顾客的采购的东西的卡片清单,如下:
Class ShoppingCartItem
PreviousItem AS shoppingCartItem
NextItem As ShoppingCartItem
ProductCode As Integer
Function GetNextItem As ShoppingCartItem
...
End Function
...
End Class
从这里,就可以看见类的结构和模式,都有相同之处--insertions(插入)、deletion(删除),以及traversal(浏览),只不过是对不同数据类型进行操作而已,显然,要维护这两段几乎具有相同函数的代码很没有必要,又一个显然的解决方案就是把对清单的处理抽象出来,做成一个它自己的类,然后,就可用这个类派生出不同数据类型的子类,如:
Class ListItem
PreviousItem AS ListItem
NextItem As ListItem
Function GetNextItem As ListItem
...
End Function
...
End Class
这样的话,只需要对ListItem类调试一次,而且用户只需要把它编译一次以后,就不再需要管理关于清单管理的代码,用户只需要使用它就可以了:
Class CustomerInfo
Inherits ListItem
ID As Integer
FullName As string
End Class
Class ShoppingCartItem
Inherits ListItem
ProductCode As Integer
End Class
3.对不同的数据类型使用相同名称的类和方法
一个决定是否要使用继承办法就是是否有以下问题:
(1)是否需要对不同的数据类型进行相似的操作;
(2)需不需(想不想)访问程序源代码;
举一个非常具有代表性的例子,就是画图的软件包,假想它可以画圆、线和长方形。一个非常简单的而且效率很高的方法就是不需要使用继承,而用Select Case语句来实现,如下:
Sub Draw(Shape As DrawingShape,X As Integer,Y AS Integer,_
Size AS Integer)
Select Case Shape.Type
Case shpCircle
'Circle drawing code here
End Case
Case shpLine
'Line drawing code here
End Case
...
End Sub
但是这样处理的话将会出一些问题,将来如果需要添加画椭圆功能的话,那么就必须重新改写代码,也许最终用户不需要访问程序的源代码,但是可能画一个椭圆还需要一些参数,因为椭圆需要的参数是一个长半径和一个短半径。但是这些参数又和其他图形的参数无关,如果现在又要画一个折线(有多条线组成),则又需要加入一些其他的参数,而且这些参数又各其他的图形的参数无关。
继承就可以非常好地解决这些问题,一个设计得很好的类可以给用户留下一下很好的空间(MustInherit方法),那样的话,每一种图形都可以满足,但是一些类的属性(诸如XY坐标)可以作为基本类的属性,因为这个属性是每一种图形都需要的,如:
Class Shape
Sub MustInherit Draw()
X As Integer
Y As Integer
End Class
其他的图形都可以继承这个类,如要画一条线,代码如下:
Class Line Extends Shape()
Length As Integer
Sub Overrides Draw
'Implement Draw here
End Sub
End Class
长方形的类,又应该如下表示:
Class Rectangle Extends Line()
Width AS Integer
Sub Overrides Draw()
'Implement Draw here
End Sub
End Class
它的继承子类(例如:长方形)能够在图象类的二进制的文件的基础上继承,而不是在基础类的代码的基础上继承。
4.基类的方法需要改变
Visual Basic(以及其他的提供继承的编程语言)使得用户有能力重载基本类的方法,只要在派生类中可以访问的基类的方法都可以被加载(除了那些NotInheritable)。假如用户感觉基本类的DisplayError方法需要给出一个错误号,而不只是错误的描述,则用户可以在派生类中改变这种方法的行为,则派生类会调用它本身的方法,而不是基类的方法。
5.通过改变一个基本类,就改变了所有的派生类
继承的一个强大的作用就是当改变一个基本类的属性时,就改变了所有的派生类的相应的结构,但是,这也是一个危险的行为--有可能改变了自己设计的基类后,别人由该基类而派生出来的派生类就会产生错误。
继承是一个非常有用的概念,但是却很容易被用得不合适,通常用接口来实现可能会更好,本文的目的就是使用户懂得怎样更好的使用类的继承。
当遇到如下情况时,继承将是一个好的选择:
(1)简化一个低等级的不使用类的API函数
(2)从基本类中得到重用的代码
(3)需要对不同的数据类型使用相同名称的类和方法
(4)类的层次相当,最多4到5级,而且不仅增加一级或两级
(5)通过改变一个基本类,就改变所有的派生类。
1.简化一个低等级的不使用类的API函数
类库使得一系列的函数的调用更加合理,特别是当调用的这些函数不是能类的形式组织的时候,举一个例子,就象那些为简化在窗体中画图表的API函数而设计的类一样,这种类库在有些时候被叫做"Wrapper"。这些把API函数包括起来的类库通常可以简化操作,不过这样做并没有企图把API函数的一些基本特性给隐藏掉。Windows Graphics API函数有很多,但用户并不需要了解它底层的一些变化。用户在设计类的时候也是这样,必须考虑类库的用户对底层的了解程序。实际上,很多类库都是根据API函数来组织的,通常它们有两个趋势:使得API函数对高级用户更加方便,或者通过高度的概括,把这些底层的信息隐含起来,这意味着把Windows API函数的具体信息对用户封装。
但是这里又有一个隐含的关于开发类库的问题,被打成包的类导致了非常多的文字说明,把API简化得越多,需要的文字说明就越多。经常性的,几百个API函数被组织成一打或一百个类,每一个类具有几十个或几百个重叠的属性、方法和事件,整理或者浏览这个类将是工作量非常大的一件事。
设计和维护一个把各种API打包的类库,是一件工作量非常大的事情,设想现在要写一个类库来简化一个为Web服务的Html的网页的生成过程,这就需要先考虑一下自己是不是需要知道"Html";是不是需要把这些诸如"tags"或"angle brackets"的概念隐含掉;是不是向用户显示一些诸如字体等这些属性;是不是只要能处理Html就行了,还是需要处理XMl,能不能叫XHtml?等等。
假设一个基本类含有一个MustOverride方法叫做ParseHtml,然而当需要支持XMl时,就必须把Html支持替换掉,此时需要把方法ParseHtml清空,惟一的选择是建立一个ParseXMl方法,但不能把ParseHtml方法移除掉,因为当把该方法移除以后,就会导致Html业务的丧失,因为整个类是分布的,用户必须让ParseHtml和ParseXml方法同时存在,但这让内存和运行时间效率都减低了。
诸如这种问题在网络开发环境还是存在的,用户可能是过早设计了Html类库,也许一个基于接口的系统可以更好的解决这个问题。它让用户建立一系列的Html接口,当在确认XHtml或者XML是更好的基底时,就可以放弃或改善Html接口。
2.从基本类中得到重用的代码
使用继承的一个重要的原因就是为了代码的重用,然而这也是最危险的。正是因为代码的重用,即使是设计的最好的系统,有的时候也会出现一些设计者难以预料的改变。
一个经典的表示代码重用的高效率的例子就是一个数据管理库,设想现在有一个大型的应用程序,用来管理几个在内存中的清单,另一个是从顾客的数据中拷贝来的清单,该数据结构可以如下所示:
Class CustomerInfo
Public PreviousCustomer As CustomerInfo
Public NextCustomer As CustomerInfo
Public ID As Integer
Public FullName As String
'Adds a CustomerInfo to list
Function Insertcustomer As CustomerInfo
...
End Function
'Removes a CustomerInfo from list
Function DeleteCustomer As CustomerInfo
...
End Function
'Obtains next CustomerInfo in list
Function GetNextCustmer As CustomerInfo
...
End Function
'Obtains previous CustomerInfo
Function GetPreCustomer As CustomerInfo
...
End Function
End Class
可能还有一张顾客的采购的东西的卡片清单,如下:
Class ShoppingCartItem
PreviousItem AS shoppingCartItem
NextItem As ShoppingCartItem
ProductCode As Integer
Function GetNextItem As ShoppingCartItem
...
End Function
...
End Class
从这里,就可以看见类的结构和模式,都有相同之处--insertions(插入)、deletion(删除),以及traversal(浏览),只不过是对不同数据类型进行操作而已,显然,要维护这两段几乎具有相同函数的代码很没有必要,又一个显然的解决方案就是把对清单的处理抽象出来,做成一个它自己的类,然后,就可用这个类派生出不同数据类型的子类,如:
Class ListItem
PreviousItem AS ListItem
NextItem As ListItem
Function GetNextItem As ListItem
...
End Function
...
End Class
这样的话,只需要对ListItem类调试一次,而且用户只需要把它编译一次以后,就不再需要管理关于清单管理的代码,用户只需要使用它就可以了:
Class CustomerInfo
Inherits ListItem
ID As Integer
FullName As string
End Class
Class ShoppingCartItem
Inherits ListItem
ProductCode As Integer
End Class
3.对不同的数据类型使用相同名称的类和方法
一个决定是否要使用继承办法就是是否有以下问题:
(1)是否需要对不同的数据类型进行相似的操作;
(2)需不需(想不想)访问程序源代码;
举一个非常具有代表性的例子,就是画图的软件包,假想它可以画圆、线和长方形。一个非常简单的而且效率很高的方法就是不需要使用继承,而用Select Case语句来实现,如下:
Sub Draw(Shape As DrawingShape,X As Integer,Y AS Integer,_
Size AS Integer)
Select Case Shape.Type
Case shpCircle
'Circle drawing code here
End Case
Case shpLine
'Line drawing code here
End Case
...
End Sub
但是这样处理的话将会出一些问题,将来如果需要添加画椭圆功能的话,那么就必须重新改写代码,也许最终用户不需要访问程序的源代码,但是可能画一个椭圆还需要一些参数,因为椭圆需要的参数是一个长半径和一个短半径。但是这些参数又和其他图形的参数无关,如果现在又要画一个折线(有多条线组成),则又需要加入一些其他的参数,而且这些参数又各其他的图形的参数无关。
继承就可以非常好地解决这些问题,一个设计得很好的类可以给用户留下一下很好的空间(MustInherit方法),那样的话,每一种图形都可以满足,但是一些类的属性(诸如XY坐标)可以作为基本类的属性,因为这个属性是每一种图形都需要的,如:
Class Shape
Sub MustInherit Draw()
X As Integer
Y As Integer
End Class
其他的图形都可以继承这个类,如要画一条线,代码如下:
Class Line Extends Shape()
Length As Integer
Sub Overrides Draw
'Implement Draw here
End Sub
End Class
长方形的类,又应该如下表示:
Class Rectangle Extends Line()
Width AS Integer
Sub Overrides Draw()
'Implement Draw here
End Sub
End Class
它的继承子类(例如:长方形)能够在图象类的二进制的文件的基础上继承,而不是在基础类的代码的基础上继承。
4.基类的方法需要改变
Visual Basic(以及其他的提供继承的编程语言)使得用户有能力重载基本类的方法,只要在派生类中可以访问的基类的方法都可以被加载(除了那些NotInheritable)。假如用户感觉基本类的DisplayError方法需要给出一个错误号,而不只是错误的描述,则用户可以在派生类中改变这种方法的行为,则派生类会调用它本身的方法,而不是基类的方法。
5.通过改变一个基本类,就改变了所有的派生类
继承的一个强大的作用就是当改变一个基本类的属性时,就改变了所有的派生类的相应的结构,但是,这也是一个危险的行为--有可能改变了自己设计的基类后,别人由该基类而派生出来的派生类就会产生错误。
栏目列表
最新更新
python爬虫及其可视化
使用python爬取豆瓣电影短评评论内容
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
uniapp/H5 获取手机桌面壁纸 (静态壁纸)
[前端] DNS解析与优化
为什么在js中需要添加addEventListener()?
JS模块化系统
js通过Object.defineProperty() 定义和控制对象
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比