- 浏览: 84214 次
- 性别:
- 来自: 北京
面向接口编程详解
2009-04-23 作者:张洋 来源:ZhangYang's Blog
本系列《面向接口编程详解》将分为三部分:
面向接口编程详解(一)——思想基础
在这一篇中,将对接口及面向接口编程有个大致的介绍,着重在于思想上的讲解。
面向接口编程详解(二)——编程实例
这一篇将结合一个实例“移动存储设备模拟”来让大家对面向接口编程有个直观印象。
面向接口编程详解(三)——模式研究
讲解几个设计模式中的面向接口思想和基于.NET平台的分层架构中的面向接口思想,加深理解。
面向接口编程详解(一)——思想基础
我想,对于各位使用面向对象编程语言的程序员来说,“接口”这个名词一定不陌生,但是不知各位有 没有这样的疑惑:接口有什么用途?它和抽象类有什么区别?能不能用抽象类代替接口呢?而且,作为程序员,一定经常听到“面向接口编程”这个短语,那么它是 什么意思?有什么思想内涵?和面向对象编程是什么关系?本文将一一解答这些疑问。
1.面向接口编程和面向对象编程是什么关系
首先,面向接口编程和面向对象编程并不是平级的,它并不是比面向对象编程更先进的一种独立的编程思想,而是附属于面向对象思想体系,属于其一部分。或者说,它是面向对象编程体系中的思想精髓之一。
2.接口的本质
接口,在表面上是由几个没有主体代码的方法定义组成的集合体,有唯一的名称,可以被类或其他接口所实现(或者也可以说继承)。它在形式上可能是如下的样子:
interface InterfaceName
{
void Method1();
void Method2( int para1);
void Method3( string para2, string para3);
}
那么,接口的本质是什么呢?或者说接口存在的意义是什么。我认为可以从以下两个视角考虑:
1)接口是一组规则的集合,它规定了实现本接口的类或接口必须拥有的一组规则。体现了自然界“如果你是……则必须能……”的理念。
例如,在自然界中,人都能吃饭,即“如果你是人,则必须能吃饭”。那么模拟到计算机程序 中,就应该有一个IPerson(习惯上,接口名由“I”开头)接口,并有一个方法叫Eat(),然后我们规定,每一个表示“人”的类,必须实现 IPerson接口,这就模拟了自然界“如果你是人,则必须能吃饭”这条规则。
从这里,我想各位也能看到些许面向对象思想的东西。面向对象思想的核心之一,就是模拟真实世界,把真实世界中的事物抽象成类,整个程序靠各个类的实例互相通信、互相协作完成系统功能,这非常符合真实世界的运行状况,也是面向对象思想的精髓。
2)接口是在一定粒度视图上同类事物的抽象表示。注意这里我强调了在一定粒度视图上,因为“同类事物”这个概念是相对的,它因为粒度视图不同而不同。
例如,在我的眼里,我是一个人,和一头猪有本质区别,我可以接受我和我同学是同类这个说 法,但绝不能接受我和一头猪是同类。但是,如果在一个动物学家眼里,我和猪应该是同类,因为我们都是动物,他可以认为“人”和“猪”都实现了 IAnimal这个接口,而他在研究动物行为时,不会把我和猪分开对待,而会从“动物”这个较大的粒度上研究,但他会认为我和一棵树有本质区别。
现在换了一个遗传学家,情况又不同了,因为生物都能遗传,所以在他眼里,我不仅和猪没区别,和一只蚊子、一个细菌、一颗树、一个蘑菇乃至一个SARS病毒都没什么区别,因为他会认为我们都实现了IDescendable这个接口(注:descend vi. 遗传),即我们都是可遗传的东西,他不会分别研究我们,而会将所有生物作为同类进行研究,在他眼里没有人和病毒之分,只有可遗传的物质和不可遗传的物质。但至少,我和一块石头还是有区别的。
可不幸的事情发生了,某日,地球上出现了一位伟大的人,他叫列宁,他在熟读马克、恩格斯的辩证唯物主义思想巨著后,颇有心得,于是他下了一个著名的定 义:所谓物质,就是能被意识所反映的客观实在。至此,我和一块石头、一丝空气、一条成语和传输手机信号的电磁场已经没什么区别了,因为在列宁的眼里,我们 都是可以被意识所反映的客观实在。如果列宁是一名程序员,他会这么说:所谓物质,就是所有同时实现了“IReflectabe”和“IEsse”两个接口 的类所生成的实例。(注:reflect v. 反映 esse n. 客观实在)
也许你会觉得我上面的例子像在瞎掰,但是,这正是接口得以存在的意义。面向对象思想和核心之一叫做多态性,什么叫多态性?说白了就是在某个粒度视图层面上 对同类事物不加区别的对待而统一处理。而之所以敢这样做,就是因为有接口的存在。像那个遗传学家,他明白所有生物都实现了IDescendable接口, 那只要是生物,一定有Descend()这个方法,于是他就可以统一研究,而不至于分别研究每一种生物而最终累死。
可能这里还不能给你一个关于接口本质和作用的直观印象。那么在后文的例子和对几个设计模式的解析中,你将会更直观体验到接口的内涵。
3.面向接口编程综述
通过上文,我想大家对接口和接口的思想内涵有了一个了解,那么什么是面向接口编程呢?我个人的定义是:在系统分析和架构中,分清层次和依赖关系,每个层次不是直接向其上层提供服务(即不是直接实例化在上层中),而是通过定义一组接口,仅向上层暴露其接口功能,上层对于下层仅仅是接口依赖,而不依赖具体类 。
这样做的好处是显而易见的,首先对系统灵活性大有好处。当下层需要改变时,只要接口及接口功能不变,则上层不用做任何修改。甚至可以在不改动上层代码时将 下层整个替换掉,就像我们将一个WD的60G硬盘换成一个希捷的160G的硬盘,计算机其他地方不用做任何改动,而是把原硬盘拔下来、新硬盘插上就行了, 因为计算机其他部分不依赖具体硬盘,而只依赖一个IDE接口,只要硬盘实现了这个接口,就可以替换上去。从这里看,程序中的接口和现实中的接口极为相似, 所以我一直认为,接口(interface)这个词用的真是神似!
使用接口的另一个好处就是不同部件或层次的开发人员可以并行开工,就像造硬盘的不用等造CPU的,也不用等造显示器的,只要接口一致,设计合理,完全可以并行进行开发,从而提高效率。
本篇文章先到这里。最后我想再啰嗦一句:面向对象的精髓是模拟现实,这也可以说是我这篇文章的灵魂。所以,多从现实中思考面向对象的东西,对提高系统分析设计能力大有脾益。
下篇文章,我将用一个实例来展示接口编程的基本方法。
而第三篇,我将解析经典设计模式中的一些面向接口编程思想,并解析一下.NET分层架构中的面向接口思想。
对本文的补充:
仔细看了各位的回复,非常高兴能和大家一起讨论技术问题。感谢给出肯定的朋友,也要感谢提出意见和质疑的朋友,这促使我更深入思考一些东西,希望能借此进步。在这里我想补充一些东西,以讨论一些回复中比较集中的问题。
1.关于“面向接口编程”中的“接口”与具体面向对象语言中“接口”两个词
看到有朋友提出“面向接口编程”中的“接口”二 字应该比单纯编程语言中的interface范围更大。我经过思考,觉得很有道理。这里我写的确实不太合理。我想,面向对象语言中的“接口”是指具体的一 种代码结构,例如C#中用interface关键字定义的接口。而“面向接口编程”中的“接口”可以说是一种从软件架构的角度、从一个更抽象的层面上指那 种用于隐藏具体底层类和实现多态性的结构部件。从这个意义上说,如果定义一个抽象类,并且目的是为了实现多态,那么我认为把这个抽象类也称为“接口”是合 理的。但是用抽象类实现多态合理不合理?在下面第二条讨论。
概括来说,我觉得两个“接口”的概念既相互区别又相互联系。“面向接口编程”中的接口是一种思想层面的用于实现多态性、提高软件灵活性和可维护性的架构部件,而具体语言中的“接口”是将这种思想中的部件具体实施到代码里的手段。
2.关于抽象类与接口
看到回复中这是讨论的比较激烈的一个问题。很抱歉我考虑不周没有在文章中讨论这个问题。我个人对这个问题的理解如下:
如果单从具体代码来看,对这两个概念很容易模糊,甚至觉得接口就是多余的,因为单从具体功能来看,除多重继承外(C#,Java中),抽象类似乎完全能取代接口。但是,难道接口的存在是为了实现多重继承?当然不是。我认为,抽象类和接口的区别在于使用动机。使用抽象类是为了代码的复用,而使用接口的动机是为了实现多态性。 所以,如果你在为某个地方该使用接口还是抽象类而犹豫不决时,那么可以想想你的动机是什么。
看到有朋友对IPerson这个接口的质疑,我个人的理解是,IPerson这个接口该不该定义,关键看具体应用中是怎么个情况。如果我们的项目中有 Women和Man,都继承Person,而且Women和Man绝大多数方法都相同,只有一个方法DoSomethingInWC()不同(例子比较粗 俗,各位见谅),那么当然定义一个AbstractPerson抽象类比较合理,因为它可以把其他所有方法都包含进去,子类只定义 DoSomethingInWC(),大大减少了重复代码量。
但是,如果我们程序中的Women和Man两个类基本没有共同代码,而且有一个PersonHandle类需要实例化他们,并且不希望知道他们是男是女,而只需把他们当作人看待,并实现多态,那么定义成接口就有必要了。
总而言之,接口与抽象类的区别主要在于使用的动机,而不在于其本身。而一个东西该定义成抽象类还是接口,要根据具体环境的上下文决定。
再者,我认为接口和抽象类的另一个区别在于,抽象类和它的子类之间应该是一般和特殊的关系,而接口仅仅是它的子类应该实现的一组规则。(当然,有时也可能 存在一般与特殊的关系,但我们使用接口的目的不在这里)如,交通工具定义成抽象类,汽车、飞机、轮船定义成子类,是可以接受的,因为汽车、飞机、轮船都是 一种特殊的交通工具。再譬如Icomparable接口,它只是说,实现这个接口的类必须要可以进行比较,这是一条规则。如果Car这个类实现了 Icomparable,只是说,我们的Car中有一个方法可以对两个Car的实例进行比较,可能是比哪辆车更贵,也可能比哪辆车更大,这都无所谓,但我 们不能说“汽车是一种特殊的可以比较”,这在文法上都不通。
面向接口编程详解(二)——编程实例
通过上一篇文章的讨论,我想各位朋友对“面接接口编程”有了一个大致的了解。那么在这一篇里,我们用一个例子,让各位对这个重要的编程思想有个直观的印象。为充分考虑到初学者,所以这个例子非常简单,望各位高手见谅。
问题的提出
定义: 现在我们要开发一个应用,模拟移动存储设备的读写,即计算机与U盘、MP3、移动硬盘等设备进行数据交换。
上下文(环境): 已知要实现U盘、MP3播放器、移动硬盘三种移动存储设备,要求计算机能同这三种设备进行数据交换,并且以后可能会有新的第三方的移动存储设备,所以计算机必须有扩展性,能与目前未知而以后可能会出现的存储设备进行数据交换。
各个存储设备间读、写的实现方法不同,U盘和移动硬盘只有这两个方法,MP3Player还有一个PlayMusic方法。
名词定义: 数据交换={读,写}
看到上面的问题,我想各位脑子中一定有了不少想法,这是个很好解决的问题,很多方案都能达到效果。下面,我列举几个典型的方案。
解决方案列举
方案一: 分 别定义FlashDisk、MP3Player、MobileHardDisk三个类,实现各自的Read和Write方法。然后在Computer类中 实例化上述三个类,为每个类分别写读、写方法。例如,为FlashDisk写ReadFromFlashDisk、WriteToFlashDisk两个 方法。总共六个方法。
方案二: 定 义抽象类MobileStorage,在里面写虚方法Read和Write,三个存储设备继承此抽象类,并重写Read和Write方法。 Computer类中包含一个类型为MobileStorage的成员变量,并为其编写get/set器,这样Computer中只需要两个方 法:ReadData和WriteData,并通过多态性实现不同移动设备的读写。
方案三: 与方案二基本相同,只是不定义抽象类,而是定义接口IMobileStorage,移动存储器类实现此接口。Computer中通过依赖接口IMobileStorage实现多态性。
方案四: 定义接口IReadable和IWritable,两个接口分别只包含Read和Write,然后定义接口IMobileStorage接口继承自IReadable和IWritable,剩下的实现与方案三相同。
下面,我们来分析一下以上四种方案:
首先,方案一最直白,实现起来最简单,但是它有一个致命的弱点:可扩展性差。或者说,不符合“开放-关闭原则”(注:意为对扩展开放,对修改关闭)。当将 来有了第三方扩展移动存储设备时,必须对Computer进行修改。这就如在一个真实的计算机上,为每一种移动存储设备实现一个不同的插口、并分别有各自 的驱动程序。当有了一种新的移动存储设备后,我们就要将计算机大卸八块,然后增加一个新的插口,在编写一套针对此新设备的驱动程序。这种设计显然不可取。
此方案的另一个缺点在于,冗余代码多。如果有100种移动存储,那我们的Computer中岂不是要至少写200个方法,这是不能接受的!
我们再来看方案二和方案三,之所以将这两个方案放在一起讨论,是因为他们基本是一个方案(从思想层面上来说),只不过实现手段不同,一个是使用了抽象类,一个是使用了接口,而且最终达到的目的应该是一样的。
我们先来评价这种方案:首先它解决了代码冗余的问题,因为可以动态替换移动设备,并且都实现了共同的接口,所以不管有多少种移动设备,只要一个Read方 法和一个Write方法,多态性就帮我们解决问题了。而对第一个问题,由于可以运行时动态替换,而不必将移动存储类硬编码在Computer中,所以有了 新的第三方设备,完全可以替换进去运行。这就是所谓的“依赖接口,而不是依赖与具体类”,不信你看看,Computer类只有一个 MobileStorage类型或IMobileStorage类型的成员变量,至于这个变量具体是什么类型,它并不知道,这取决于我们在运行时给这个变 量的赋值。如此一来,Computer和移动存储器类的耦合度大大下降。
那么这里该选抽象类还是接口呢?还记得第一篇文章我对抽象类和接口选择的建议吗?看动机。这里,我们的动机显然是实现多态性而不是为了代码复用,所以当然要用接口。
最后我们再来看一看方案四,它和方案三很类似,只是将“可读”和“可写”两个规则分别抽象成了接口,然后让IMobileStorage再继承它们。这样 做,显然进一步提高了灵活性,但是,这有没有设计过度的嫌疑呢?我的观点是:这要看具体情况。如果我们的应用中可能会出现一些类,这些类只实现读方法或只 实现写方法,如只读光盘,那么这样做也是可以的。如果我们知道以后出现的东西都是能读又能写的,那这两个接口就没有必要了。其实如果将只读设备的 Write方法留空或抛出异常,也可以不要这两个接口。总之一句话:理论是死的,人是活的,一切从现实需要来,防止设计不足,也要防止设计过度。
在这里,我们姑且认为以后的移动存储都是能读又能写的,所以我们选方案三。
实现
下面,我们要将解决方案加以实现。我选择的语言是C#,但是在代码中不会用到C#特有的性质,所以使用其他语言的朋友一样可以参考。
首先编写IMobileStorage接口:
1 namespace InterfaceExample
2 {
3 public interface IMobileStorage
4 {
5 void Read(); //从自身读数据
6 void Write(); //将数据写入自身
7 }
8 }
比较简单,只有两个方法,没什么好说的,接下来是三个移动存储设备的具体实现代码:
U盘
1 namespace InterfaceExample
2 {
3 public class FlashDisk : IMobileStorage
4 {
5 public void Read()
6 {
7 Console.WriteLine( "Reading from FlashDisk……" );
8 Console.WriteLine( "Read finished!" );
9 }
10
11 public void Write()
12 {
13 Console.WriteLine( "Writing to FlashDisk……" );
14 Console.WriteLine( "Write finished!" );
15 }
16 }
17 }
MP3
1 namespace InterfaceExample
2 {
3 public class MP3Player : IMobileStorage
4 {
5 public void Read()
6 {
7 Console.WriteLine( "Reading from MP3Player……" );
8 Console.WriteLine( "Read finished!" );
9 }
10
11 public void Write()
12 {
13 Console.WriteLine( "Writing to MP3Player……" );
14 Console.WriteLine( "Write finished!" );
15 }
16
17 public void PlayMusic()
18 {
19 Console.WriteLine( "Music is playing……" );
20 }
21 }
22 }
移动硬盘
1 namespace InterfaceExample
2 {
3 public class MobileHardDisk : IMobileStorage
4 {
5 public void Read()
6 {
7 Console.WriteLine( "Reading from MobileHardDisk……" );
8 Console.WriteLine( "Read finished!" );
9 }
10
11 public void Write()
12 {
13 Console.WriteLine( "Writing to MobileHardDisk……" );
14 Console.WriteLine( "Write finished!" );
15 }
16 }
17 }
可以看到,它们都实现了IMobileStorage接口,并重写了各自不同的Read和Write方法。下面,我们来写Computer:
1 namespace InterfaceExample
2 {
3 public class Computer
4 {
5 private IMobileStorage _usbDrive;
6
7 public IMobileStorage UsbDrive
8 {
9 get
10 {
11 return this ._usbDrive;
12 }
13 set
14 {
15 this ._usbDrive = value;
16 }
17 }
18
19 public Computer()
20 {
21 }
22
23 public Computer(IMobileStorage usbDrive)
24 {
25 this .UsbDrive = usbDrive;
26 }
27
28 public void ReadData()
29 {
30 this ._usbDrive.Read();
31 }
32
33 public void WriteData()
34 {
35 this ._usbDrive.Write();
36 }
37 }
38 }
其中的UsbDrive就是可替换的移动存储设备,之所以用这个名字,是为了让大家觉得直观,就像我们平常使用电脑上的USB插口插拔设备一样。
OK!下面我们来测试我们的“电脑”和“移动存储设备”是否工作正常。我是用的C#控制台程序,具体代码如下:
1 namespace InterfaceExample
2 {
3 class Program
4 {
5 static void Main( string [] args)
6 {
7 Computer computer = new Computer();
8 IMobileStorage mp3Player = new MP3Player();
9 IMobileStorage flashDisk = new FlashDisk();
10 IMobileStorage mobileHardDisk = new MobileHardDisk();
11
12 Console.WriteLine( "I inserted my MP3 Player into my computer and copy some music to it:" );
13 computer.UsbDrive = mp3Player;
14 computer.WriteData();
15 Console.WriteLine();
16
17 Console.WriteLine( "Well,I also want to copy a great movie to my computer from a mobile hard disk:" );
18 computer.UsbDrive = mobileHardDisk;
19 computer.ReadData();
20 Console.WriteLine();
21
22 Console.WriteLine( "OK!I have to read some files from my flash disk and copy another file to it:" );
23 computer.UsbDrive = flashDisk;
24 computer.ReadData();
25 computer.WriteData();
26 Console.ReadLine();
27 }
28 }
29 }
现在编译、运行程序,如果没有问题,将看到如下运行结果:
好的,看来我们的系统工作良好。
后来……
刚过了一个星期,就有人送来了新的移动存储设备NewMobileStorage,让我测试能不能用,我微微一笑,心想这不是小菜一碟,让我们看看面向接口编程的威力吧!将测试程序修改成如下:
1 namespace InterfaceExample
2 {
3 class Program
4 {
5 static void Main( string [] args)
6 {
7 Computer computer = new Computer();
8 IMobileStorage newMobileStorage = new NewMobileStorage();
9
10 Console.WriteLine( "Now,I am testing the new mobile storage:" );
11 computer.UsbDrive = newMobileStorage;
12 computer.ReadData();
13 computer.WriteData();
14 Console.ReadLine();
15 }
16 }
17 }
编译、运行、看结果:
哈哈,神奇吧,Computer一点都不用改动,就可以使新的设备正常运行。这就是所谓“对扩展开放,对修改关闭”。
又过了几天,有人通知我说又有一个叫SuperStorage的移动设备要接到我们的Computer上,我心想来吧,管你是“超级存储”还是“特级存储”,我的“面向接口编程大法”把你们统统搞定。
但是,当设备真的送来,我傻眼了,开发这个新设备的团队没有拿到我们的IMobileStorage接口,自然也没有遵照这个约定。这个设备的读、写方法 不叫Read和Write,而是叫rd和wt,这下完了……不符合接口啊,插不上。但是,不要着急,我们回到现实来找找解决的办法。我们一起想想:如果你 的Computer上只有USB接口,而有人拿来一个PS/2的鼠标要插上用,你该怎么办?想起来了吧,是不是有一种叫“PS/2-USB”转换器的东 西?也叫适配器,可以进行不同接口的转换。对了!程序中也有转换器。
这里,我要引入一个设计模式,叫“Adapter”。它的作用就如现实中的适配器一样,把接口不一致的两个插件接合起来。由于本篇不是讲设计模式的,而且Adapter设计模式很好理解,所以我就不细讲了,先来看我设计的类图吧:
如图所示,虽然SuperStorage没有实现IMobileStorage,但我们定义了一个实现IMobileStorage的 SuperStorageAdapter,它聚合了一个SuperStorage,并将rd和wt适配为Read和 Write,SuperStorageAdapter的具体代码如下:
1 namespace InterfaceExample
2 {
3 public class SuperStorageAdapter : IMobileStorage
4 {
5 private SuperStorage _superStorage;
6
7 public SuperStorage SuperStorage
8 {
9 get
10 {
11 return this ._superStorage;
12 }
13 set
14 {
15 this ._superStorage = value;
16 }
17 }
18
19 public void Read()
20 {
21 this ._superStorage.rd();
22 }
23
24 public void Write()
25 {
26 this ._superStorage.wt();
27 }
28 }
29 }
好,现在我们来测试适配过的新设备,测试代码如下:
1 namespace InterfaceExample
2 {
3 class Program
4 {
5 static void Main( string [] args)
6 {
7 Computer computer = new Computer();
8 SuperStorageAdapter superStorageAdapter = new SuperStorageAdapter();
9 SuperStorage superStorage = new SuperStorage();
10 superStorageAdapter.SuperStorage = superStorage;
11
12 Console.WriteLine( "Now,I am testing the new super storage with adapter:" );
13 computer.UsbDrive = superStorageAdapter;
14 computer.ReadData();
15 computer.WriteData();
16 Console.ReadLine();
17 }
18 }
19 }
运行后会得到如下结果:
OK!虽然遇到了一些困难,不过在设计模式的帮助下,我们还是在没有修改Computer任何代码的情况下实现了新设备的运行。
好了,理论在第一篇讲得足够多了,所以这里我就不多讲了。希望各位朋友结合第一篇的理论和这个例子,仔细思考面向接口的问题。当然,不要忘了结合现实。
下一篇,我将解析经典设计模式中的面向接口编程思想和.NET平台分层架构中接口的运用。
2009-04-23 作者:张洋 来源:ZhangYang's Blog
本系列《面向接口编程详解》将分为三部分:
面向接口编程详解(一)——思想基础
在这一篇中,将对接口及面向接口编程有个大致的介绍,着重在于思想上的讲解。
面向接口编程详解(二)——编程实例
这一篇将结合一个实例“移动存储设备模拟”来让大家对面向接口编程有个直观印象。
面向接口编程详解(三)——模式研究
讲解几个设计模式中的面向接口思想和基于.NET平台的分层架构中的面向接口思想,加深理解。
面向接口编程详解(一)——思想基础
我想,对于各位使用面向对象编程语言的程序员来说,“接口”这个名词一定不陌生,但是不知各位有 没有这样的疑惑:接口有什么用途?它和抽象类有什么区别?能不能用抽象类代替接口呢?而且,作为程序员,一定经常听到“面向接口编程”这个短语,那么它是 什么意思?有什么思想内涵?和面向对象编程是什么关系?本文将一一解答这些疑问。
1.面向接口编程和面向对象编程是什么关系
首先,面向接口编程和面向对象编程并不是平级的,它并不是比面向对象编程更先进的一种独立的编程思想,而是附属于面向对象思想体系,属于其一部分。或者说,它是面向对象编程体系中的思想精髓之一。
2.接口的本质
接口,在表面上是由几个没有主体代码的方法定义组成的集合体,有唯一的名称,可以被类或其他接口所实现(或者也可以说继承)。它在形式上可能是如下的样子:
interface InterfaceName
{
void Method1();
void Method2( int para1);
void Method3( string para2, string para3);
}
那么,接口的本质是什么呢?或者说接口存在的意义是什么。我认为可以从以下两个视角考虑:
1)接口是一组规则的集合,它规定了实现本接口的类或接口必须拥有的一组规则。体现了自然界“如果你是……则必须能……”的理念。
例如,在自然界中,人都能吃饭,即“如果你是人,则必须能吃饭”。那么模拟到计算机程序 中,就应该有一个IPerson(习惯上,接口名由“I”开头)接口,并有一个方法叫Eat(),然后我们规定,每一个表示“人”的类,必须实现 IPerson接口,这就模拟了自然界“如果你是人,则必须能吃饭”这条规则。
从这里,我想各位也能看到些许面向对象思想的东西。面向对象思想的核心之一,就是模拟真实世界,把真实世界中的事物抽象成类,整个程序靠各个类的实例互相通信、互相协作完成系统功能,这非常符合真实世界的运行状况,也是面向对象思想的精髓。
2)接口是在一定粒度视图上同类事物的抽象表示。注意这里我强调了在一定粒度视图上,因为“同类事物”这个概念是相对的,它因为粒度视图不同而不同。
例如,在我的眼里,我是一个人,和一头猪有本质区别,我可以接受我和我同学是同类这个说 法,但绝不能接受我和一头猪是同类。但是,如果在一个动物学家眼里,我和猪应该是同类,因为我们都是动物,他可以认为“人”和“猪”都实现了 IAnimal这个接口,而他在研究动物行为时,不会把我和猪分开对待,而会从“动物”这个较大的粒度上研究,但他会认为我和一棵树有本质区别。
现在换了一个遗传学家,情况又不同了,因为生物都能遗传,所以在他眼里,我不仅和猪没区别,和一只蚊子、一个细菌、一颗树、一个蘑菇乃至一个SARS病毒都没什么区别,因为他会认为我们都实现了IDescendable这个接口(注:descend vi. 遗传),即我们都是可遗传的东西,他不会分别研究我们,而会将所有生物作为同类进行研究,在他眼里没有人和病毒之分,只有可遗传的物质和不可遗传的物质。但至少,我和一块石头还是有区别的。
可不幸的事情发生了,某日,地球上出现了一位伟大的人,他叫列宁,他在熟读马克、恩格斯的辩证唯物主义思想巨著后,颇有心得,于是他下了一个著名的定 义:所谓物质,就是能被意识所反映的客观实在。至此,我和一块石头、一丝空气、一条成语和传输手机信号的电磁场已经没什么区别了,因为在列宁的眼里,我们 都是可以被意识所反映的客观实在。如果列宁是一名程序员,他会这么说:所谓物质,就是所有同时实现了“IReflectabe”和“IEsse”两个接口 的类所生成的实例。(注:reflect v. 反映 esse n. 客观实在)
也许你会觉得我上面的例子像在瞎掰,但是,这正是接口得以存在的意义。面向对象思想和核心之一叫做多态性,什么叫多态性?说白了就是在某个粒度视图层面上 对同类事物不加区别的对待而统一处理。而之所以敢这样做,就是因为有接口的存在。像那个遗传学家,他明白所有生物都实现了IDescendable接口, 那只要是生物,一定有Descend()这个方法,于是他就可以统一研究,而不至于分别研究每一种生物而最终累死。
可能这里还不能给你一个关于接口本质和作用的直观印象。那么在后文的例子和对几个设计模式的解析中,你将会更直观体验到接口的内涵。
3.面向接口编程综述
通过上文,我想大家对接口和接口的思想内涵有了一个了解,那么什么是面向接口编程呢?我个人的定义是:在系统分析和架构中,分清层次和依赖关系,每个层次不是直接向其上层提供服务(即不是直接实例化在上层中),而是通过定义一组接口,仅向上层暴露其接口功能,上层对于下层仅仅是接口依赖,而不依赖具体类 。
这样做的好处是显而易见的,首先对系统灵活性大有好处。当下层需要改变时,只要接口及接口功能不变,则上层不用做任何修改。甚至可以在不改动上层代码时将 下层整个替换掉,就像我们将一个WD的60G硬盘换成一个希捷的160G的硬盘,计算机其他地方不用做任何改动,而是把原硬盘拔下来、新硬盘插上就行了, 因为计算机其他部分不依赖具体硬盘,而只依赖一个IDE接口,只要硬盘实现了这个接口,就可以替换上去。从这里看,程序中的接口和现实中的接口极为相似, 所以我一直认为,接口(interface)这个词用的真是神似!
使用接口的另一个好处就是不同部件或层次的开发人员可以并行开工,就像造硬盘的不用等造CPU的,也不用等造显示器的,只要接口一致,设计合理,完全可以并行进行开发,从而提高效率。
本篇文章先到这里。最后我想再啰嗦一句:面向对象的精髓是模拟现实,这也可以说是我这篇文章的灵魂。所以,多从现实中思考面向对象的东西,对提高系统分析设计能力大有脾益。
下篇文章,我将用一个实例来展示接口编程的基本方法。
而第三篇,我将解析经典设计模式中的一些面向接口编程思想,并解析一下.NET分层架构中的面向接口思想。
对本文的补充:
仔细看了各位的回复,非常高兴能和大家一起讨论技术问题。感谢给出肯定的朋友,也要感谢提出意见和质疑的朋友,这促使我更深入思考一些东西,希望能借此进步。在这里我想补充一些东西,以讨论一些回复中比较集中的问题。
1.关于“面向接口编程”中的“接口”与具体面向对象语言中“接口”两个词
看到有朋友提出“面向接口编程”中的“接口”二 字应该比单纯编程语言中的interface范围更大。我经过思考,觉得很有道理。这里我写的确实不太合理。我想,面向对象语言中的“接口”是指具体的一 种代码结构,例如C#中用interface关键字定义的接口。而“面向接口编程”中的“接口”可以说是一种从软件架构的角度、从一个更抽象的层面上指那 种用于隐藏具体底层类和实现多态性的结构部件。从这个意义上说,如果定义一个抽象类,并且目的是为了实现多态,那么我认为把这个抽象类也称为“接口”是合 理的。但是用抽象类实现多态合理不合理?在下面第二条讨论。
概括来说,我觉得两个“接口”的概念既相互区别又相互联系。“面向接口编程”中的接口是一种思想层面的用于实现多态性、提高软件灵活性和可维护性的架构部件,而具体语言中的“接口”是将这种思想中的部件具体实施到代码里的手段。
2.关于抽象类与接口
看到回复中这是讨论的比较激烈的一个问题。很抱歉我考虑不周没有在文章中讨论这个问题。我个人对这个问题的理解如下:
如果单从具体代码来看,对这两个概念很容易模糊,甚至觉得接口就是多余的,因为单从具体功能来看,除多重继承外(C#,Java中),抽象类似乎完全能取代接口。但是,难道接口的存在是为了实现多重继承?当然不是。我认为,抽象类和接口的区别在于使用动机。使用抽象类是为了代码的复用,而使用接口的动机是为了实现多态性。 所以,如果你在为某个地方该使用接口还是抽象类而犹豫不决时,那么可以想想你的动机是什么。
看到有朋友对IPerson这个接口的质疑,我个人的理解是,IPerson这个接口该不该定义,关键看具体应用中是怎么个情况。如果我们的项目中有 Women和Man,都继承Person,而且Women和Man绝大多数方法都相同,只有一个方法DoSomethingInWC()不同(例子比较粗 俗,各位见谅),那么当然定义一个AbstractPerson抽象类比较合理,因为它可以把其他所有方法都包含进去,子类只定义 DoSomethingInWC(),大大减少了重复代码量。
但是,如果我们程序中的Women和Man两个类基本没有共同代码,而且有一个PersonHandle类需要实例化他们,并且不希望知道他们是男是女,而只需把他们当作人看待,并实现多态,那么定义成接口就有必要了。
总而言之,接口与抽象类的区别主要在于使用的动机,而不在于其本身。而一个东西该定义成抽象类还是接口,要根据具体环境的上下文决定。
再者,我认为接口和抽象类的另一个区别在于,抽象类和它的子类之间应该是一般和特殊的关系,而接口仅仅是它的子类应该实现的一组规则。(当然,有时也可能 存在一般与特殊的关系,但我们使用接口的目的不在这里)如,交通工具定义成抽象类,汽车、飞机、轮船定义成子类,是可以接受的,因为汽车、飞机、轮船都是 一种特殊的交通工具。再譬如Icomparable接口,它只是说,实现这个接口的类必须要可以进行比较,这是一条规则。如果Car这个类实现了 Icomparable,只是说,我们的Car中有一个方法可以对两个Car的实例进行比较,可能是比哪辆车更贵,也可能比哪辆车更大,这都无所谓,但我 们不能说“汽车是一种特殊的可以比较”,这在文法上都不通。
面向接口编程详解(二)——编程实例
通过上一篇文章的讨论,我想各位朋友对“面接接口编程”有了一个大致的了解。那么在这一篇里,我们用一个例子,让各位对这个重要的编程思想有个直观的印象。为充分考虑到初学者,所以这个例子非常简单,望各位高手见谅。
问题的提出
定义: 现在我们要开发一个应用,模拟移动存储设备的读写,即计算机与U盘、MP3、移动硬盘等设备进行数据交换。
上下文(环境): 已知要实现U盘、MP3播放器、移动硬盘三种移动存储设备,要求计算机能同这三种设备进行数据交换,并且以后可能会有新的第三方的移动存储设备,所以计算机必须有扩展性,能与目前未知而以后可能会出现的存储设备进行数据交换。
各个存储设备间读、写的实现方法不同,U盘和移动硬盘只有这两个方法,MP3Player还有一个PlayMusic方法。
名词定义: 数据交换={读,写}
看到上面的问题,我想各位脑子中一定有了不少想法,这是个很好解决的问题,很多方案都能达到效果。下面,我列举几个典型的方案。
解决方案列举
方案一: 分 别定义FlashDisk、MP3Player、MobileHardDisk三个类,实现各自的Read和Write方法。然后在Computer类中 实例化上述三个类,为每个类分别写读、写方法。例如,为FlashDisk写ReadFromFlashDisk、WriteToFlashDisk两个 方法。总共六个方法。
方案二: 定 义抽象类MobileStorage,在里面写虚方法Read和Write,三个存储设备继承此抽象类,并重写Read和Write方法。 Computer类中包含一个类型为MobileStorage的成员变量,并为其编写get/set器,这样Computer中只需要两个方 法:ReadData和WriteData,并通过多态性实现不同移动设备的读写。
方案三: 与方案二基本相同,只是不定义抽象类,而是定义接口IMobileStorage,移动存储器类实现此接口。Computer中通过依赖接口IMobileStorage实现多态性。
方案四: 定义接口IReadable和IWritable,两个接口分别只包含Read和Write,然后定义接口IMobileStorage接口继承自IReadable和IWritable,剩下的实现与方案三相同。
下面,我们来分析一下以上四种方案:
首先,方案一最直白,实现起来最简单,但是它有一个致命的弱点:可扩展性差。或者说,不符合“开放-关闭原则”(注:意为对扩展开放,对修改关闭)。当将 来有了第三方扩展移动存储设备时,必须对Computer进行修改。这就如在一个真实的计算机上,为每一种移动存储设备实现一个不同的插口、并分别有各自 的驱动程序。当有了一种新的移动存储设备后,我们就要将计算机大卸八块,然后增加一个新的插口,在编写一套针对此新设备的驱动程序。这种设计显然不可取。
此方案的另一个缺点在于,冗余代码多。如果有100种移动存储,那我们的Computer中岂不是要至少写200个方法,这是不能接受的!
我们再来看方案二和方案三,之所以将这两个方案放在一起讨论,是因为他们基本是一个方案(从思想层面上来说),只不过实现手段不同,一个是使用了抽象类,一个是使用了接口,而且最终达到的目的应该是一样的。
我们先来评价这种方案:首先它解决了代码冗余的问题,因为可以动态替换移动设备,并且都实现了共同的接口,所以不管有多少种移动设备,只要一个Read方 法和一个Write方法,多态性就帮我们解决问题了。而对第一个问题,由于可以运行时动态替换,而不必将移动存储类硬编码在Computer中,所以有了 新的第三方设备,完全可以替换进去运行。这就是所谓的“依赖接口,而不是依赖与具体类”,不信你看看,Computer类只有一个 MobileStorage类型或IMobileStorage类型的成员变量,至于这个变量具体是什么类型,它并不知道,这取决于我们在运行时给这个变 量的赋值。如此一来,Computer和移动存储器类的耦合度大大下降。
那么这里该选抽象类还是接口呢?还记得第一篇文章我对抽象类和接口选择的建议吗?看动机。这里,我们的动机显然是实现多态性而不是为了代码复用,所以当然要用接口。
最后我们再来看一看方案四,它和方案三很类似,只是将“可读”和“可写”两个规则分别抽象成了接口,然后让IMobileStorage再继承它们。这样 做,显然进一步提高了灵活性,但是,这有没有设计过度的嫌疑呢?我的观点是:这要看具体情况。如果我们的应用中可能会出现一些类,这些类只实现读方法或只 实现写方法,如只读光盘,那么这样做也是可以的。如果我们知道以后出现的东西都是能读又能写的,那这两个接口就没有必要了。其实如果将只读设备的 Write方法留空或抛出异常,也可以不要这两个接口。总之一句话:理论是死的,人是活的,一切从现实需要来,防止设计不足,也要防止设计过度。
在这里,我们姑且认为以后的移动存储都是能读又能写的,所以我们选方案三。
实现
下面,我们要将解决方案加以实现。我选择的语言是C#,但是在代码中不会用到C#特有的性质,所以使用其他语言的朋友一样可以参考。
首先编写IMobileStorage接口:
1 namespace InterfaceExample
2 {
3 public interface IMobileStorage
4 {
5 void Read(); //从自身读数据
6 void Write(); //将数据写入自身
7 }
8 }
比较简单,只有两个方法,没什么好说的,接下来是三个移动存储设备的具体实现代码:
U盘
1 namespace InterfaceExample
2 {
3 public class FlashDisk : IMobileStorage
4 {
5 public void Read()
6 {
7 Console.WriteLine( "Reading from FlashDisk……" );
8 Console.WriteLine( "Read finished!" );
9 }
10
11 public void Write()
12 {
13 Console.WriteLine( "Writing to FlashDisk……" );
14 Console.WriteLine( "Write finished!" );
15 }
16 }
17 }
MP3
1 namespace InterfaceExample
2 {
3 public class MP3Player : IMobileStorage
4 {
5 public void Read()
6 {
7 Console.WriteLine( "Reading from MP3Player……" );
8 Console.WriteLine( "Read finished!" );
9 }
10
11 public void Write()
12 {
13 Console.WriteLine( "Writing to MP3Player……" );
14 Console.WriteLine( "Write finished!" );
15 }
16
17 public void PlayMusic()
18 {
19 Console.WriteLine( "Music is playing……" );
20 }
21 }
22 }
移动硬盘
1 namespace InterfaceExample
2 {
3 public class MobileHardDisk : IMobileStorage
4 {
5 public void Read()
6 {
7 Console.WriteLine( "Reading from MobileHardDisk……" );
8 Console.WriteLine( "Read finished!" );
9 }
10
11 public void Write()
12 {
13 Console.WriteLine( "Writing to MobileHardDisk……" );
14 Console.WriteLine( "Write finished!" );
15 }
16 }
17 }
可以看到,它们都实现了IMobileStorage接口,并重写了各自不同的Read和Write方法。下面,我们来写Computer:
1 namespace InterfaceExample
2 {
3 public class Computer
4 {
5 private IMobileStorage _usbDrive;
6
7 public IMobileStorage UsbDrive
8 {
9 get
10 {
11 return this ._usbDrive;
12 }
13 set
14 {
15 this ._usbDrive = value;
16 }
17 }
18
19 public Computer()
20 {
21 }
22
23 public Computer(IMobileStorage usbDrive)
24 {
25 this .UsbDrive = usbDrive;
26 }
27
28 public void ReadData()
29 {
30 this ._usbDrive.Read();
31 }
32
33 public void WriteData()
34 {
35 this ._usbDrive.Write();
36 }
37 }
38 }
其中的UsbDrive就是可替换的移动存储设备,之所以用这个名字,是为了让大家觉得直观,就像我们平常使用电脑上的USB插口插拔设备一样。
OK!下面我们来测试我们的“电脑”和“移动存储设备”是否工作正常。我是用的C#控制台程序,具体代码如下:
1 namespace InterfaceExample
2 {
3 class Program
4 {
5 static void Main( string [] args)
6 {
7 Computer computer = new Computer();
8 IMobileStorage mp3Player = new MP3Player();
9 IMobileStorage flashDisk = new FlashDisk();
10 IMobileStorage mobileHardDisk = new MobileHardDisk();
11
12 Console.WriteLine( "I inserted my MP3 Player into my computer and copy some music to it:" );
13 computer.UsbDrive = mp3Player;
14 computer.WriteData();
15 Console.WriteLine();
16
17 Console.WriteLine( "Well,I also want to copy a great movie to my computer from a mobile hard disk:" );
18 computer.UsbDrive = mobileHardDisk;
19 computer.ReadData();
20 Console.WriteLine();
21
22 Console.WriteLine( "OK!I have to read some files from my flash disk and copy another file to it:" );
23 computer.UsbDrive = flashDisk;
24 computer.ReadData();
25 computer.WriteData();
26 Console.ReadLine();
27 }
28 }
29 }
现在编译、运行程序,如果没有问题,将看到如下运行结果:
好的,看来我们的系统工作良好。
后来……
刚过了一个星期,就有人送来了新的移动存储设备NewMobileStorage,让我测试能不能用,我微微一笑,心想这不是小菜一碟,让我们看看面向接口编程的威力吧!将测试程序修改成如下:
1 namespace InterfaceExample
2 {
3 class Program
4 {
5 static void Main( string [] args)
6 {
7 Computer computer = new Computer();
8 IMobileStorage newMobileStorage = new NewMobileStorage();
9
10 Console.WriteLine( "Now,I am testing the new mobile storage:" );
11 computer.UsbDrive = newMobileStorage;
12 computer.ReadData();
13 computer.WriteData();
14 Console.ReadLine();
15 }
16 }
17 }
编译、运行、看结果:
哈哈,神奇吧,Computer一点都不用改动,就可以使新的设备正常运行。这就是所谓“对扩展开放,对修改关闭”。
又过了几天,有人通知我说又有一个叫SuperStorage的移动设备要接到我们的Computer上,我心想来吧,管你是“超级存储”还是“特级存储”,我的“面向接口编程大法”把你们统统搞定。
但是,当设备真的送来,我傻眼了,开发这个新设备的团队没有拿到我们的IMobileStorage接口,自然也没有遵照这个约定。这个设备的读、写方法 不叫Read和Write,而是叫rd和wt,这下完了……不符合接口啊,插不上。但是,不要着急,我们回到现实来找找解决的办法。我们一起想想:如果你 的Computer上只有USB接口,而有人拿来一个PS/2的鼠标要插上用,你该怎么办?想起来了吧,是不是有一种叫“PS/2-USB”转换器的东 西?也叫适配器,可以进行不同接口的转换。对了!程序中也有转换器。
这里,我要引入一个设计模式,叫“Adapter”。它的作用就如现实中的适配器一样,把接口不一致的两个插件接合起来。由于本篇不是讲设计模式的,而且Adapter设计模式很好理解,所以我就不细讲了,先来看我设计的类图吧:
如图所示,虽然SuperStorage没有实现IMobileStorage,但我们定义了一个实现IMobileStorage的 SuperStorageAdapter,它聚合了一个SuperStorage,并将rd和wt适配为Read和 Write,SuperStorageAdapter的具体代码如下:
1 namespace InterfaceExample
2 {
3 public class SuperStorageAdapter : IMobileStorage
4 {
5 private SuperStorage _superStorage;
6
7 public SuperStorage SuperStorage
8 {
9 get
10 {
11 return this ._superStorage;
12 }
13 set
14 {
15 this ._superStorage = value;
16 }
17 }
18
19 public void Read()
20 {
21 this ._superStorage.rd();
22 }
23
24 public void Write()
25 {
26 this ._superStorage.wt();
27 }
28 }
29 }
好,现在我们来测试适配过的新设备,测试代码如下:
1 namespace InterfaceExample
2 {
3 class Program
4 {
5 static void Main( string [] args)
6 {
7 Computer computer = new Computer();
8 SuperStorageAdapter superStorageAdapter = new SuperStorageAdapter();
9 SuperStorage superStorage = new SuperStorage();
10 superStorageAdapter.SuperStorage = superStorage;
11
12 Console.WriteLine( "Now,I am testing the new super storage with adapter:" );
13 computer.UsbDrive = superStorageAdapter;
14 computer.ReadData();
15 computer.WriteData();
16 Console.ReadLine();
17 }
18 }
19 }
运行后会得到如下结果:
OK!虽然遇到了一些困难,不过在设计模式的帮助下,我们还是在没有修改Computer任何代码的情况下实现了新设备的运行。
好了,理论在第一篇讲得足够多了,所以这里我就不多讲了。希望各位朋友结合第一篇的理论和这个例子,仔细思考面向接口的问题。当然,不要忘了结合现实。
下一篇,我将解析经典设计模式中的面向接口编程思想和.NET平台分层架构中接口的运用。
发表评论
-
组合or继承
2013-05-27 11:54 872到底使用组合还是继承是每本讲设计的资料里都要讨论一番的话题 ... -
Java访问控制private之我见
2013-05-24 11:36 821最近待业在家,遂有空重新读了thinking in Java这 ... -
XML 系列教程
2012-05-06 12:50 613http://www.w3school.com.cn/x.as ... -
浅析java回调机制与观察者模式
2012-04-10 17:23 18731 java回调机制: 首先解 ... -
Java程序设计之-复合优先于继承
2012-04-03 10:33 1494组合 通过创建一个由其他对象组合的对象来获得新功能的重用方法 ... -
java学习之路(转)
2012-03-30 15:01 823(一) 从事软件 ... -
java内部类
2012-03-28 16:26 907一、 定义 放在一个类的内部的类我们就叫内部类。 二、 作用 ... -
为什么匿名内部类只能访问其所在方法中的final变量(转)
2012-03-28 15:45 1098(1).所谓“局部内部类”就是在对象的方法成员内部定义的类。而 ... -
Java访问权限修饰符(转)
2012-03-28 11:20 11111、Class类的访问权限: ... -
【java】好书推荐
2012-03-26 15:31 1468Java软件架构师所要需的东西 作为Java程序员来说,最痛 ... -
Java绝对好文,转载的!(转载)
2012-03-25 14:45 824想来学习Java也有两个年头了,永远不敢说多么精通,但也想谈谈 ... -
理解java动态加载机制
2012-03-20 00:01 10411.java动态性 java是一种 ... -
热部署、热加载
2012-03-19 14:14 3699不重启Tomcat有两种方式:热部署、热加载 热部署:容 ... -
Registry of Singleton 模式(转)
2012-03-06 10:01 800考虑使用 Singleton 模式 时拥有子类别的问题,在Si ... -
单例模式(Singleton Pattern)
2012-03-05 20:40 7056.单例模式(Singleton Pattern) 前面说提到 ... -
java.util.concurrent 多线程框架
2012-02-26 16:15 810http://daoger.iteye.com/blog/14 ... -
线程----BlockingQueue (转),java
2012-02-26 13:50 822/** 本例介绍一个 ... -
关于多个线程同时调用单例模式的对象,该对象中方法的局部变量是否会受多个线程的影响
2012-02-12 12:16 2963对于那些会以多线程运行的单例类,例如Web应用中的Servle ... -
Java线程同步机制synchronized关键字的理解
2011-12-25 14:34 791由于同一进程的多个线 ... -
synchronized与static synchronized 的区别
2011-12-24 22:48 6801.synchronized与static synchroni ...
相关推荐
面向接口编程详解(二)——编程实例 面向接口编程是一种编程思想,强调通过接口来实现多态性和可扩展性。在本文中,我们将通过一个实例来详细解释面向接口编程的思想和优点。 问题提出:我们要开发一个应用,模拟...
面向接口编程详解.chm面向接口编程详解.chm面向接口编程详解.chm
面向接口编程是一种编程范式,它基于面向对象编程的思想,但更强调通过接口来定义对象的行为,而不是具体实现。接口在这里扮演着规范和契约的角色,定义了一组方法签名,但不包含任何实现代码。这种编程方式允许代码...
面向接口编程是面向对象编程中的一个重要概念,它并非与面向对象编程平级,而是面向对象思想的精华之一。本文将详细解释面向接口编程的思想基础。 首先,我们需要理解接口的定义和本质。接口在编程中是一个包含一...
面向接口编程是一种编程范式,它是面向对象编程(OOP)的一个重要组成部分,而非独立的编程思想。在面向接口编程中,我们关注的是定义清晰、明确的行为规范,而不是具体的实现细节。接口作为一种契约,规定了类必须...
面向接口编程是一种重要的软件设计原则,它强调程序的组件应通过接口进行交互,而不是直接依赖于具体的实现。在本文档的第三部分,作者探讨了如何在实际的模式中运用这一原则,通过分析MVC(Model-View-Controller)...
### 面向接口编程详解 #### 一、面向接口编程与面向对象编程的关系 面向接口编程并非一种独立于面向对象编程之外的新编程思想,而是面向对象编程思想体系中的一个重要组成部分。面向对象编程的核心在于模拟现实...
面向接口编程是软件工程中的一种重要设计理念,尤其在面向对象编程中扮演着核心角色。它不仅深化了面向对象思想的应用,而且为构建更加灵活、可扩展和可维护的软件系统提供了理论依据。本文旨在深入探讨面向接口编程...
"Java面向对象编程实例详解2.txt"和"Java面向对象编程实例详解.txt"可能包含了详细的讲解和示例代码,涵盖了类的创建、对象的实例化、访问控制、构造函数、抽象类和接口、异常处理、集合框架等方面的知识。...
面向对象编程是Python的核心特性之一,它通过模拟现实世界的实体和关系来构建代码结构。本文主要探讨了Python中面向对象编程的基本概念、特点以及如何在实际应用中进行类和对象的创建。 首先,面向对象编程(OOP)...
《Java2编程详解》这本书是Java开发者的重要参考资料,它涵盖了Java语言的核心概念和技术,旨在帮助读者深入理解并熟练掌握Java2平台的编程技术。在这个压缩包中,包含了一个名为"Java2编程详解.pdf"的PDF文件,这很...
Java编程详解是一个深入探讨Java语言及其应用的领域,特别是针对最新的Java版本。在这个最新的Java编程详解中,我们可能涵盖了许多现代Java开发的关键知识点,包括但不限于以下几个方面: 1. **Java语言基础**:从...
库卡机器人编程详解 本文档为库卡机器人编程的详细指南,涵盖了库卡机器人系统软件(KSS)版本4.1的编程方法和技术。本文档面向专业的机器人开发人员和编程人员,旨在帮助他们更好地理解和应用库卡机器人编程技术。...
2. **面向对象编程**:Java是一种纯面向对象的语言,书中会详细介绍类、对象、封装、继承和多态等概念,以及如何设计和实现接口,这对于理解Java的编程模型至关重要。 3. **异常处理**:Java中的异常处理机制是一个...
根据提供的信息,《java编程详解》是一本被广泛推荐并深受读者喜爱的专业书籍,它旨在为初学者和进阶学习者提供全面、深入的Java编程知识。以下是对该书可能涵盖的一些核心知识点的概述: ### Java语言基础 1. **...