哪个高手能给我解释一下:类和接口的区别。
所属论坛: .NET技术 VB.NET
发表时间: 2006-4-2 20:54:38
哪个高手能给我解释一下:类和接口的区别。
第1楼 2006-4-2 21:09:00
接口是用来表达比较抽象的功能,不能拿来直接用(不能被实例化)
类用来表达比较具体的功能,除抽象类外其它都能直接拿来用(实例化)
第2楼 2006-4-2 21:15:36
在功能方面有什么具体的区别吗?
第3楼 2006-4-2 21:32:30
不知道,学习
第4楼 2006-4-2 21:46:25
接口可以用来继承,类可以继承接口的功能
第5楼 2006-4-2 22:40:19
我最想知道的是,既然有类了,为什么还要用接口呢。
好象接口能实现的类就可以实现啊
第6楼 2006-4-2 22:42:17
定义和实现分离
尤其是.net 或Java里 是单根继承的情况下
第7楼 2006-4-2 22:44:54
其实你说的也没错,接口能实现的大多数情况下类也可以实现
但是一个类可以继承多个接口,这是基类所不具备的功能.
另外,接口主要用于写比较抽象的内容.这也有利于代码的可维护性和可读性
不知道我说的对不对,欢迎楼下的批我
第8楼 2006-4-3 8:14:40
和类一样,接口也可以定义属性、方法和事件。与类不同的是,接口并不提供实现。现在的接口,是由类来实现的,并从类中被定义为单独的实体
第10楼 2006-4-3 8:35:25
接口只是定义属性、方法事件,不实现具体代码
其实这些东西类也能模仿,最主要的是在继承上
.NET只允许继承一个类,当你需要继承多个类的时候,就只能靠接口了
第12楼 2006-4-3 9:20:03
Henry的VB.NET之旅(八)—接口
“还有两种继承方法?”我是真正被VB.NET的强大功能所折服了。求知的渴望驱使着我向大李露出了一个最灿烂的笑容,“行了,别傻笑了,我告诉你不就成了。”大李不禁也笑了起来。
“刚才我说到‘脆弱的基类’时,就提到实现继承最大的问题,就在于基类与派生类之间的关系过于紧密,还记得吧?基类实现细节往往会泄露出来,这不是我们愿意看到的封装情况,所以有很多程序设计师就想尽方法来改进这一问题,其中最出名的,就是COM了。”
“COM?我在VB6中经常用呀,是一种组件形式呀。”我不是太明白大李的话,这和面向对象的继承有什么关系?
“你说的不错,正是通过组件的这种封装方式,我们就可以把实现继承局限于在组件内部使用,而我们使用组件时,就不用理会它内部是什么,怎么实现的。这就可以避免不可预测地对基类的修改。我们把利用组件的组织程序方法称为面向组件程序设计,但这也是一种面向对象的设计方法,不过是更具强制性。组件支持组件内部的实现继承,还支持接口继承。”
“接口继承,我不是太清楚。不过,接口我是清楚的,就是组件开放的属性、方法与事件、公用变量的定义方法。我在VB6中也接触过接口编程,不是太方便,好象要把定义了方法却没有写实现过程的类编译成DLL文件,VB6会自动将它创建为接口,不过只能是隐藏的,不是显式定义的。”我回忆了一下说。
大李微微点了点头说:“从面向对象的观点来看,接口就是对象的外观,而对象实际的工作方式就是实现。把接口与实现分离开就是我们要进行封装的动机。用户只能通过接口来操作,但是看不到具体的实现的代码。”
大李顿了一顿,然后接着说:“VB.NET 以前的 Visual Basic 版本可以使用接口,但不能直接创建它们。VB.NET 却是允许可以用 Interface 语句定义真正的接口的喔!”
此言一出,真让我大吃一惊。“我们也可以直接定义接口吗?”
“当然,”大李说,“在VB.NET中,和类一样,接口也可以定义属性、方法和事件。但正如我刚才说到的,与类不同的是,接口并不提供实现。现在的接口,是由类来实现的,并从类中被定义为单独的实体。”
大李手指在桌面上重重的敲了一下,加强了一下语气:“我们可以这样来理解,接口表示的是一种约定。实现接口的类必须严格按其定义来实现接口的每个方面。有了接口,就可以将功能定义为一些紧密相关成员的小组。可以在不危害现有代码的情况下,开发接口的增强型实现,从而使兼容性问题最小化。也可以在任何时候通过开发附加接口和实现来添加新的功能。虽然接口实现可以进化,但接口本身一旦被发布就不能再更改。对已发布的接口进行更改会破坏现有的代码。若把接口视为约定,很明显约定双方都各有其承担的义务。接口的发布者同意不再更改该接口,接口的实现者则同意严格按设计来实现接口。”
“也就是说,在VB.NET中,接口是用类来实现的,就象是个抽象类,只是关键字用的是Interface,不是class,对吗?”我还是很好奇。
“接口的实现可以是类,也可以是结构。接口的定义用的是Interface关键字,实现时用的是Implements关键字”大李淡淡的一句话,使我在心中开始回忆起类和结构的差别来(详见前文《类和结构》)。
大李接着跟我解说:“接口的成员包括其成员声明引入的成员和从其基接口继承的成员。只有嵌套类型、方法、属性和事件才能作为接口成员。方法和属性不能有实体。接口成员隐式地定为是Public,而且不能指定访问修饰符。接口自已倒是可以添加修饰符。”大李跟着看了我一眼,就在电脑上开始写起示例来:
Public Interface IHenry
Sub subX(ByVal x As Integer)
Function fcnY(ByVal y As Integer) As Long
Property proZ() As String
End Interface
Public Class CHenry
Implements IHenry
Private z1 As String
Sub subX(ByVal x As Integer) Implements IHenry.subX
'填写实现代码
End Sub
Function fcnY(ByVal y As Integer) As Long Implements IHenry.fcnY
'填写实现代码
End Function
Property proZ() As String Implements IHenry.proZ
Get
Return z1
End Get
Set(ByVal Value As String)
z1 = Value
End Set
End Property
End Class
大李指着代码说:“你看,我用Interface定义了一个接口IHenry(笔者注:一般来说,接口的命名第一个字母为I,这并没有什么强制的含义,但却是一个通用的命名规则),内含三个方法与属性的定义,但并没有实现;实现代码写在CHenry类中。你可以按我刚才说的约定的思路来理解,IHenry接口其实就是一个合约的提纲,CHenry是该合约的一个操作版本,只要在CHenry中实现了接口IHenry定义的所有的方法,不管是怎么实现的,有没有加入新的方法,都可认为CHenry是支持IHenry接口的一个实现类。”
“一个实现类?也就是说接口可以有多个实现喽?”我不解地问。
“当然,我刚才说过,接口其实就是一个对象的外观,在VB.NET中有很多很重要的接口,定义了很多种类型的对象,比如说你所熟悉的Windows Form的控件,它们的基类大部分是Component类,而Component类就是IComponent接口的一个实现类,IComponent类还有其它三个实现类,那就是:Control、HttpApplication和MarshalByValueComponent类,分别完成不同的功能,但它们都要实现IComponent接口所定义的方法与属性,且参数定义与返回类型都要与接口定义时的签名一致。换个角度来看这个问题,我们如何创建一个组件,且让系统都识别,靠的就是对接口的实现。要成为组件的类必须实现 IComponent 接口,并提供不需要参数或只需一个类型为 IContainer 的参数的基本构造函数就行了。”
“哦,我明白了,通过接口,我们可以定义下某种对象的基本外观,然后可以自由地进行实现与扩展,却不涉及对原型的直接修改。太棒了!”我一下子高兴了起来。
“是呀,在VB.NET中可以显式定义接口,使得接口编程也成为很棒的编程方式了。刚才所说的,在不同的类中实现同一个接口,不就是一种用接口实现的多态性吗?另外,在类和结构中也可以实现多个接口。”大李写了如下的代码给我看:
Interface IOne
Sub SubOne()
End Interface
Interface ITwo
Sub SubTwo()
End Interface
Class CHenry
Implements IOne
Implements ITwo
Sub SubOne() Implements IOne.SubOne
'实现代码
End Sub
Sub SubTwo() Implements ITwo.SubTwo
'实现代码
End Sub
End Class
“真有意思,”我饶有兴致地看着代码,“也就是说CHenry就具备了IOne与ITwo所定义的外观特点了。”
我一下子想起了抽象类的定义,不由好奇地说:“如果把接口看成一个基类,那么用于实现的类不就可以看成是它的一个派生类了?这是不是就是接口继承呀?
第13楼 2006-4-3 9:20:18
Henry的VB.NET之旅(十)—何时用接口
韩睿
大李没告诉我接口与抽象类的区别,什么时候用接口,什么时候用实现继承。弄得我中饭也没吃好,老在琢磨这事,这不,一吃完饭,我就冲上楼,一个房间一个房间转,到处找大李。
过了好一阵,这老哥才和几个同事说说笑笑地回到办公室,我立即走上前,半请半拉地把他拽到电脑旁。“大李哥,我实在想得头晕,既然在VB.NET中接口有了这么大的发展空间,在形式上与抽象类如此相似,那么它们有什么区别?什么时候用接口呢?”
听着我一连串的问题,大李微笑着摇摇头,拍拍我的肩膀说:“小兄弟,不光是你弄不清,其实就是很有经验的程序设计师也对什么时候用接口,什么时候用抽象类而头痛咧。”
此话一出,我更是疑惑重重。不过反而安下心来,老鸟们都弄不清的问题,我不清楚也不必心中不安了。哈……
大李看着我忽忧忽喜的表情露出了一丝诧异,不过他没有理会,继续说:“但是这个问题我们还是有必要好好分析一下,让我们更明白接口与抽象类的具体含义。”
“我们早说过,抽象类是一种不能实例化而必须从中继承的类。抽象类可以完全实现,但更常见的是部分实现或者根本不实现,从而封装继承类的通用功能,它可以提供已实现的成员,因此,可以用抽象类确保特定数量的相同功能,但不能用接口这样做。”
“接口是完全抽象的成员集合,它的成员都无法在接口定义时实现,我们可以将它看作是为操作定义合同,接口的实现完全留给开发者去做。它们之间的区别,如果认真分析,还是有不少的:在VB.NET中,类只能是从一个基类继承,所以如果要使用抽象类为一组类提供多态性,这些类必须都是从那个类继承的;接口就不一样了,它不但可以用一个类或结构实现多个接口,一个接口还可以有多个实现。”
“也就是说,它们在提供多态性的问题上是有差别的?”我好象听懂了点什么。
“这是一个重要的区别,我们也可以从多态性的角度来考虑是要使用接口还是用抽象类。”大李同意了我的观点,“如果预计要创建组件的多个版本,我们应该创建抽象类。这是因为,抽象类提供简单易行的方法来控制组件版本。通过更新基类,所有继承类都随更改自动更新。这是好处,当然也是问题,对吧?(详见前文《脆弱的基类》)另一方面,接口一旦创建就不能更改。如果需要接口的新版本,必须创建一个全新的接口。所以,如果创建的功能将在大范围的全异对象间使用,则使用接口。”
我想了一下,接着大李的话说:“能不能这样说,抽象类主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。”
大李对我伸出了大拇指:“不错,小伙子悟性很好呀!你想,我上午跟你说,要创建控件,首先就是要对一些接口进行实现以让系统能够识别(详见前文《接口》)。而各个控件之间的联系其实关联性并不大。所以,它们的基础大都是接口。但是,我们要注意一点,在组件设计时,如果要在组件的所有实现间提供通用的已实现功能,则使用抽象类。这是因为我们刚才说过的原因,抽象类允许部分实现类,而接口不包含任何成员的实现。”
“唔,明白了,它们之间的区别有点明白了。”我默默地点了点头。
“另外,有个通用的设计思想,如果要设计小而简练的功能块,则使用接口。如果要设计大的功能单元,则使用抽象类。”大李又补充了一条建议。
“看来设计的问题还是蛮大的,一般来说,怎么设计接口呢?”我接着问。
“为什么你所看到的编程书籍也好,程序例程也好,极少有对接口的描述,而对类实现继承的例子比比皆是?这就从一个侧面给我们提了一个醒,如果使用适当,接口可以成为很有用的工具。但如果使用不当,它们会变得非常棘手,甚至妨碍有效的编程。接口的设计与使用其实是一项高明的艺术。”大李郑重其事的说。
“艺术?”我惊叫了一声。
“没错,艺术!”大李又加重了一下语气,“通过接口与实现的方式,我们可以将同一类型的程序运用在不同的对象上面,而且不必修改原有类,相对子程序必须通过修改源程序代码才能够达到重用的目的,接口与实现不仅是伟大的进步,也是境界极高的程序设计艺术。”
“哦,这倒是真的。”我回想起今天看到的接口的例程。
“但是,最大的问题还是集中在接口设计上。”大李接着说,“接口一旦被定义和接受,就必须保持不变,以保护为使用该接口而编写的应用程序。接口发布后,就不能对其进行更改。这是我们进行组件设计的一个重要原则,叫做‘接口不变性’。”
我点了点头:“接口不变性,这个我可以理解了。”
“我已经反反复复强调过,创建一个接口就是在创建一个定义,接口定义发布后则永远不能更改。接口不变性,就是为了保护为使用接口而编写的现有系统。当接口设计与需求有所出入,确认需要大幅度变更时,我们应该创建新的接口。一般命名方式是,新接口可以通过在原来的接口名称后追加一个数字‘2’来命名,以显示出它与现有接口的关系。然后通过接口继承来创建新接口。”
“可是,如果需求分析得不好,岂不是会出现一大堆的派生接口了?”我不免有点顾虑。
“这是肯定的,而且过于频繁地生成新接口,会因未使用的接口实现而使组件变得很庞大。有经验的设计师,在充分分析需求后,设计出的接口往往很小且相互独立,减少了性能问题发生的可能。”
“这种分解能力倒真的是艺术呀!”我不禁为之叹服。
“当然,一般来说,我们会把确定哪些属性和方法属于接口的设计过程称为‘接口分解’。基本准则是,应在一个接口中将紧密相关的几个功能聚合为一组。功能太多会使接口不便于运行,而过于细分功能将导致额外的系统开销并降低使用的简易性。掌握分解的这个度的能力是需要不断的在技术上进行磨炼,以及在对每个项目深入分析与理解的基础上才能得到的。”
“明白了。”我大声地回答着,真希望自己能早一天成为接口设计大师。
大李笑着拍了拍我:“明白了就好。其实,与设计接口相比,创建大的实现继承树更容易出错。”
“当然,”我脑海里浮现起实现继承的各个环节,“这是不是说,在某些时候适当使用接口还是很有益的。”
“看来你真的明白了,那你再来说一下,接口与类实现继承相比,好处有什么?”大李回过身开始找茶杯。
我低下头,努力地转动了一下脑子:“我试着说一下吧,总体而言,接口是一种非常有效的编程方法,它让对象的定义与实现分离,从而可以在不破坏现有应用程序的情况下使对象得以完善与进化。接口消除了实现继承的一个大问题,就是在对设计实施后再对其进行更改时很可能对代码产生破坏。即使实现继承允许类从基类继承实现,在类首次发布时仍然会使我们不得不为设计做很多的抉择。如果原有的设想不正确,并非总可以在以后的版本中对代码进行安全的更改。比如,我们定义了一个基类的方法,它需要一个 Integer 参数,而后来又确定该参数应该为 Long 数据类型。我们无法安全更改原始类,因为为从原始类派生的类所设计的应用程序可能无法进行正确编译。这一问题会扩大化,因为单个基类会影响几百个子类。”
“那用重载原始类并采用一个Long类型的参数,不就能解决这个问题了吗?”大李提了个问题。
“这个么?”我想了一下,“可是,这样不一定能达到满意的效果,因为一个派生类可能需要对采用整数的方法进行重写,如果取 Long 数据类型的方法不能被重写,该派生类可能无法正常运行。”
“那用接口怎么做?”大李不依不挠地继续问。
“办法就是发布接受新数据类型的更新接口。”我一下子就回答出来了。
“看来你已经掌握了接口操作的基本环节了。”大李的评语真让我高兴。“我再帮你总结一下,使用接口继承而不用类继承的主要原因有:在应用程序要求很多可能不相关的对象类型以提供某种功能的情况下,用接口适用性会更强;接口比基类更灵活,因为可以定义单个实现来实现多个接口;在无需从基类继承实现的情况下,接口更好;在无法使用类继承的情况下接口是很有用的。例如,结构无法从类继承,但它们可以实现接口。”
我抿着嘴用力点了点头,同时在心里默默地记忆着大李所说的每一条准则。
“回去好好想想,多写几个小程序来练习一下,明天我们还要欣赏VB.NET提供的强大的可视继承的表现呢。”