关于为什么要写一个接口,再写一个接口实现类的问题

很多人包括我自己作为初学者一直有个困惑就是:例如定义了一个接口,但是我在继承这个接口的类中还要写接口的实现方法,那我不如直接就在这个类中写实现方法岂不是更便捷,还省去了定义接口?所以说接口到底有啥用呢?知道看一篇资深大佬的解答,才茅塞顿开。接口就是个招牌。比如说你今年放假出去杭州旅游,玩了一上午,你也有点饿了,突然看到前面有个店子,上面挂着KFC,然后你就知道今天中饭有着落了。KFC就是接口,我们看到了这个接口,就知道这个店会卖炸鸡腿(实现接口)。那么为神马我们要去定义一个接口涅,这个店可以直接卖炸鸡腿啊(直接写实现方法),是的,这个店可以直接卖炸鸡腿,但没有挂KFC的招牌,我们就不能直接简单粗暴的冲进去叫服务员给两个炸鸡腿了。要么,我们就要进去问,你这里卖不卖炸鸡腿啊,卖不卖汉堡啊,卖不卖圣代啊(这就是反射)。很显然,这样一家家的问实在是非常麻烦(反射性能很差)。要么,我们就要记住,中山路108号卖炸鸡,黄山路45号卖炸鸡(硬编码),很显然这样我们要记住的很多很多东西(代码量剧增),而且,如果有新的店卖炸鸡腿,我们也不可能知道(不利于扩展)。
原作者:Ivony
链接:https://www.zhihu.com/question/20111251/answer/16585393

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JS JSP ASP .NET J2AM API接口和返回的版本 目前所有版本的JS JSP ASP .NET J2AM 都是提供源代码的,对于一些脚本语言来说,直接解压缩之后就可以使用了,不需要什么安装步骤。另外一些需要编译的语言,则提供了编译用的 shell 文件(Linux/Unix 下使用)和 bat 文件(Windows 下使用),或者直接提供编译好的二进制库文件。 不过为了让读者能够更清楚如何安装,我们还是对每种语言的安装都做详细的讲解,你可以在安装列表里找到你感兴趣的语言的安装方法。 示例 如果你已经把 JS JSP ASP .NET J2AM 安装好了,那么接下来就让我们开始第一个小程序吧。按照惯例,第一个演示程序几乎总是 HelloWorld,我们也不想打破这个惯例,不过对于 PHPRPC 来说,有服务器端就要有客户端,否则我们就没有什么好演示的啦,所以我们的第一个演示程序实际上是两个,一个是服务器端,另一个是客户端。我们都先用 PHP 语言来好了。 服务器端 view plaincopy to clipboardprint? <?php include ("php/phprpc_server.php"); function HelloWorld() { return 'Hello World!'; } $server = new PHPRPC_Server(); $server->add('HelloWorld'); $server->start(); ?> 客户端 view plaincopy to clipboardprint? <?php include ("php/phprpc_client.php"); $client = new PHPRPC_Client('http://127.0.0.1/server.php'); echo $client->HelloWorld(); ?> 对于服务器端程序,我们应该将它命名为 server.php(这是因为客户端调用时用的是这个名字,而不是 PHPRPC 的什么规定),然后把它放在本地 Web 服务器的根目录下,并保证服务器可以正常运行 PHP 程序,之后在浏览器或命令行下运行客户端程序,你就可以看到结果了。 这两个程序几乎简单到无需解释的地步,所以如果你已经明白它们的意思,那么就可以直接跳过下面的解释,继续看后面的例子。 服务器端第 1 句是将 它的服务器端程序包含到你的程序里,之后的 2 - 4 句是定义一个远程调用的函数,你会发现它与本地函数没有任何区别。第 5 句是创建服务器端对象,第 6 句是添加要发布的方法,这里添加的就是刚刚定义的 HelloWorld 函数,在 PHP 中,添加的发布方法是函数名的字符串表示,在其它语言中可能略有不同。第 7 句是启动服务。 客户端就更简单了,第 1 句是将 它的客户端程序包含到你的程序里。第 2 句是创建客户端对象,其中的参数就是服务器端的地址。第 3 句是对远程方法(函数)的调用,之后通过 echo 将它显示出来。如果顺利的话,执行后你就会看到输出的 Hello World!。 上面的例子是发布的是函数,下面我们来看一下中的静态方法如何发布: view plaincopy to clipboardprint? <?php include ("php/phprpc_server.php"); class Hello { static function HelloWorld() { return 'Hello World!'; } } $server = new PHPRPC_Server(); $server->add('HelloWorld', 'Hello'); $server->start(); ?> 这个服务器端只要它的名字与发布的地址与上面那个发布函数的例子一样的话,上面的那个客户端就可以得到同样的结果,也就是说,在客户端看来是没有任何区别的。 它并不是只可以在 PHP 中使用,它同样支持其它语言的服务器和客户端,而且还可以无差别的相互调用。 现在我们来看一下如何在 Java 中调用这个 PHP 的服务器方法: view plaincopy to clipboardprint? import org.phprpc.*; interface IHello { public String helloWorld(); } public class HelloWorld
用c++/qt的项目,项目都经测试过,真实可靠,可供自己学习c++/qt。Qt是一个用标准C++编的跨平台开发库,它对标准C++进行了扩展,引入了元对象系统、信号与槽、属性等特性,使应用程序的开发变得更高效。 Qt库中大量的以模块形式分组织的,包括基本模块和扩展模块等。一个模块通常就是一个编程主题,如数据库、图表、网络等。 一、Qt核心特点 1.1.概述 Qt本身并不是一种编程语言,它本质上是一个跨平台的C++开发库,是用标准C++编库,它为开发GUI应用程序和非GUI应用程序提供了各种。 Qt对标准C++进行了扩展,引入了一些新概念和功能,例如信号和槽、对象属性等。Qt的元对象编译器(Meta-Object Compiler,MOC)是一个预处理器,在源程序被编译前先将这些Qt特性的程序转换为标准C++兼容的形式,然后再由标准C++编译器进行编译。这就是为什么在使用信号与槽机制的里,必须添加一个Q_OBJECT宏的原因,只有添加了这个宏,moc才能对里的信号与槽的代码进行预处理。 Qt Core模块是Qt库的核心,所有其他模块都依赖于此模块,如果使用qmake来构建项目,Qt Core模块则是被自动加入的。 Qt为C++语言增加的特性就是在Qt Core模块里实现的,这些扩展特性由Qt的元对象系统实现,包括信号与槽机制、属性系统、动态型转换等。 1.2.元对象系统 Qt的元对象系统(Meta-Object-System)提供了对象之间通信的信号与槽机制、运行时型信息和动态属性系统。 元对象系统由以下三个基础组成: 1.QObject是所有使用元对象系统的的基; 2.在一个的private部分声明Q_OBJECT宏,使得可以使用元对象的特性,如动态属性、信号与槽。 3.MOC(元对象编译器)为每个QObject的子提供必要的代码来实现元对象系统的特征。 构建项目时,MOC工具读取C++源文件,当它发现的定义里有Q_OBJECT宏时,它就会为这个生成另外一个包含有元对象支持代码的C++源文件,这个生成的源文件连同实现文件一起被编译和连接。 除了信号和槽机制外,元对象还提供如下一些功能。 1.QObject::metaObject()函数返回关联的元对象,元对象QMetaObject包含了访问元对象的一些接口函数,例如QMetaObject::className()函数可在运行时返回的名称字符串。 QObject obj=new QPushButton; obj->metaObject()->className(); 2.QMetaObject::newInstance()函数创建一个新的实例。 3.QObject::inherits(const charclassName)函数判断一个对象实例是否是名称为className的或QObject的子的实例。 1.3.属性系统 1.属性定义 Qt提供一个Q_PROPERTY()宏可以定义属性,它也是属于元对象系统实现的。Qt的属性系统与C++编译器无关,可以用任何标准的C++编译器编译定义了属性的Qt C++程序。 2.属性的使用 不管是否用READ和WRITE定义了接口函数,只要知道属性名称,就可以通过QObject::property()读取属性值,并通过QObject::setProperty()设置属性值。 3.动态属性 QObject::setProperty()函数可以在运行时为定义一个新的属性,称之为动态属性。动态属性是针对的实例定义的。 动态属性可以使用QObject::property()查询,就如在定义里用Q_PROPERTY宏定义的属性一样。 例如,在数据表编辑界面上,一些字段是必填字段,就可以在初始化界面时为这些字段的关联显示组件定义一个新的required属性,并设置值为“true"。 4.的附加信息 属性系统还有一个宏Q_CLASSINFO(),可以为的元对象定义”名称——值“信息。
一个就是使用继承。比方说,你可以先创建一个颜色处理器的。 package{ public class colorProcessor{ public function setFillColor(color:uint):void{ } } } 然后,形状和文本就可以继承colorProcessor了。 package{ public class shapeClass extends colorProcessor{ override public function setFillColor(color:uint):void{ //.... } //由于还可以设置线条,你可以在这里新加一个方法 public function setLineColor(color:uint):void{ //.... } } } package{ public class textClass extends colorProcessor{ override public function setFillColor(color:uint):void{ //.... } } } 因为对文本和对形状的填充颜色设置可能会采用不同的实现方法,比方说,中国人吃饭和外国人吃饭都是吃饭,但是中国人可能用筷子,外国人则用刀叉。所以,在colorProcessor里,setFillColor就没有包含方法的实现了,给被继承的自我扩充。 第二种方法,使用接口。把colorProcessor接口。 package{ public interface IColorProcessor{ functon setFillColor(color:uint):void } } 然后形状和文本则改成 package{ public class shapeClass implements IColorProcessor{ public function setFillColor(color:uint):void{ //.... } //由于还可以设置线条,你可以在这里新加一个方法 public function setLineColor(color:uint):void{ //.... } } } package{ public class textClass implements IColorProcessor{ public function setFillColor(color:uint):void{ //.... } } } 这 么看起来,colorProcessor和IColorProcessor接口没有太大区别。这两个都声明了方法,也没有包含方法的实现。使用继承父 的,则通过覆盖方法来实现被继承的方法,而实现接口的则在接口实现出了方法的实现。像colorProcessor里这种只声明方法,里面实际 上没有方法实现,实际上是运用了抽象的思想。不过,在AS3里尚不可自定义抽象,所以,所谓的抽象也只是有形无实。真正的抽象接口一样,不 能实例化,而且,继承者必须覆盖抽象的所有方法才可以实例化(所以这点跟接口也很相似)。AS3有内置的抽象如 DisplayObjectContainer,大家可以尝试去用来测试实例化,继承的可行性。 说到这里,其实还是没有说明接口存在的必要性。显 然,上面的形状和文本,即使没有“抽象”和接口,两个照样可以正常运行。但是,假若现在加入了MC,MC不具备设置颜色的属性,那么,在Flash 的IDE下,你使用颜料桶工具将无法对MC进行颜色填充,如果你要开发一个Flash的IDE,那么,你就将要对你选中的对象进行判断,它是文本,形状, 还是MC。 在AS3里,与对象型有关的运算主要有以下几种: getQualifiedClassName getQualifiedSuperclassName is instanceof(还是推荐用is代替) as getQuailiedClassName可以获取该对象的型,返回的是名。假若my_txt是文本,my_shape是形状,那么,就有 getQualifiedClassName(my_txt)将返回textClass getQualifiedClassName(my_shape)将返回shapeClass 那么,在仅有这三种对象存在,并且该三种对象没有扩展的时候,判断被选定对象可以用if或者switch,如 switch(getQuailiedClassName(currentObj)){ case "shapeClass": case "textClass": currentObj.setFillColor(newColor); break; case "mcClass": //do nothing break; } 但是,文本还可以有动态文本,静态文本,输入文本等子,形状可能有矩形,圆形等子,那么,你目前还是有办法可以处理这个问题: switch(getQuailiedClassName(currentObj)){ case "shapeClass": case "textClass": case "triangleClass": case "rectangleClass": case "dynamicTextClass": case "staticTextClass": case "inputTextClass": currentObj.setFillColor(newColor); break; case "mcClass": //do nothing break; } 多麻烦啊~而且当继承结构复杂的时候,都不知道该多少句了。因此,getQualifiedSuperclassName就在这里起了点作用。可以检查其超是否为shapeClass或者textClass。 但 是,这个函数只能检查到上一级的名,若继承结构复杂,可能有的继承两至三级甚至更多,在不知道继承级别的情况下,用 getQualifiedSuperclassName想知道对象的继承关系链里是否存在textClass或者shapeClass,就只能通过遍历至 顶级来检验了......不但麻烦,而且效率低。 is运算符诞生啦!这一运算符可以检验某对象是否为指定的实例,只要指定在继承关系链中,都返回true。另外也包括接口。也就是说,假设my_spr是一个Sprite的实例,那么下面的三个表达式都输出true trace(my_spr is Sprite); trace(my_spr is DisplayObjectContainer); trace(my_spr is IEventDispatcher); 回到刚才说的那个选定对象的问题。假设舞台上现在既有MC,又有形状,也有文本。而且形状有圆形,矩形等子的实例,文本有静态文本和动态文本。那么,当选定一个对象时,要确定该对象是否具有颜色填充的功能,就可以将上面的代码简化为: if((currentObj is textClass) or (currentObj is shapeClass)){ currentObj.setFillColor(newColor); }else if(currentObj is mcClass){ //do nothing } 代码得到了简化,可惜的是,其适应性还是相当有限,如果可以进行颜色填充的也有很多,不止textClass和shapeClass的话,此段代码还是要重复很多很长的“排比句”。 这时,接口起到了作用。因为textClass和shapeClass都是IColorProcessor的接口实现,所以,按照is的运算规则,上面的代码就可以简化成 if((currentObj is IColorProcessor)){ currentObj.setFillColor(newColor); }else if(currentObj is mcClass){ //do nothing } 那么,只要具有填充颜色功能的实现IColorProcessor接口,就返回true,就可以进行颜色填充,而不需要再检查具体是什么实现接口了,也不用考虑继承关系,多方便。 既然如此,那么为什么不能用“抽象”代替接口呢?如果textClass和shapeClass都继承colorProcessor,然后检查currentObj is colorProcessor不也一样嘛?接口有何种特性是抽象不具备的呢? 至此,我终于没办法了,要翻Java的技术文章来看,了解下接口和抽象的区别所在。 在我所看到的文章中,貌似都认为抽象的优势比接口还大,不过却推荐使用接口,而不用抽象。看到那些文章后面的地方,终于茅塞顿开啦~~ 在讲接口的优势之前,我先继续刚才的Flash IDE开发问题。 现在,假设你开发到Flash 8,要添加上滤镜功能,但是滤镜只可以加在文本和MC上,那么,你可以先定义一个“抽象”: package{ public class filterProcessor{ public function setFilters(filt_arr:Array):void{ } } } 然后由mcClass来继承: package{ public class mcClass extends filterProcessor{ override public function setFilters(filt_arr:Array):void{ } } } 至于文本,同样地,可以用: package{ public class textClass extends filterProcessor{ override public function setFilters(filt_arr:Array):void{ } } } 文本这里就出问题了,这么,之前文本继承的colorProcessor就没有了。但是,在AS3里,你不能这么: package{ public class textClass extends filterProcessor extends colorProcessor{ override public function setFilters(filt_arr:Array):void{ } } } 也不可以成: package{ public class textClass extends filterProcessor,colorProceessor{ override public function setFilters(filt_arr:Array):void{ } } } 那 么,如果想同时继承这两个,该怎么办呢?你不可以说先让colorProcessor和filterProcessor相互继承,假如颜色处理器继承了 滤镜处理器,那将意味着,形状也可以设置滤镜(这就错掉啦)。如果反过来,就会导致MC可以进行颜色填充(也不行啊)。 在这种情况下,接口的优势就体现出来啦。 原来,接口和抽象相比,多出的一个优势在于(仅限Java和AS3),一个可以实现多个接口,但是不能继承多个。所以,如果在这里改用接口,就一切都好解决了。先定义两个接口: package{ public interface IColorProcessor{ function setFillColor(color:uint):void; } } package{ public interface IFilterProcessor{ function setFilters(filt_arr:Array):void; } } 然后,形状,文本和MC就分别用如下的方式实现接口: package{ public class shapeClass implements IColorProcessor{ public function setFillColor(color:uint):void{ } } } package{ public class textClass implements IColorProcessor,IFilterProcessor{ public function setFillColor(color:uint):void{ } } } package{ public class mcClass implements IFilterProcessor{ public function setFillColor(color:uint):void{ } } } 这样实现接口以后,在舞台上假如形状,MC,文本都存在的话,你也不需要检查他们是什么了,只要了解他们实现接口就OK。 if(currentObj is IColorProcessor){ (currentObj as IColorProcessor).setFillColor(newColor); } if(currentObj is IFilterProcessor){ (currentObj as IFilterProcessor).setFilters(filt_arr); } 可 见,定义了接口,在处理多种型的对象过程中会方便很多(可能有人会说,假若方法真的不存在,用try...catch不一样可以处理掉嘛.....,不 过......用这样的处理错误方法,在对象多的时候,运行起来的状况会怎样呢?)。从研究接口用处的过程中,我们发现,接口的产生其实是源于Java和 AS3对多态(多继承)的限制。为了可以更好地对的特性进行描述,判断处理,接口就显得相当有必要了。 _____________________________________ 讲到现在,我发现自己似乎还没有讲明白问题。但是这个时候我MS已经想到了一个更为实际的例子: 刚 讲了中国人和外国人都继承了人,现在,假设要对中国人和外国人再一次进行分,都按性别再分别对中国人和外国人进行分。假如全部用来做的话,就是先 有一个最顶级的人,接着就是中国人和外国人,男人和女人,接着就是中国男人,中国女人,外国男人,外国女人。 前面说了,AS3和Java不能继承多个(前面没看的也没关系,现在你知道就可以了)。所以,你的继承可以有两种策略,不过,顶级的人是无可争议的了。 package{ public class person{ } } 然后,第一种策略,就是先把人分成中国人和外国人: package{ public class Chinese extends person{ } } package{ public class Foreigner extends person{ } } 接着再把他们分别分成男和女的: package{ public class Chinese_man extends Chinese{ } } package{ public class Chinese_woman extends Chinese{ } } package{ public class Foreign_man extends Foreigner{ } } package{ public class Foreign_woman extends Foreigner{ } } 第二种策略,跟第一种策略相反。先按性别分,再按地区分: package{ public class man extends person{ } } package{ public class woman extends person{ } } 接下来的就是 package{ public class Chinese_man extends man{ } } package{ public class Chinese_woman extends woman{ } } package{ public class Foreign_man extends man{ } } package{ public class Foreign_woman extends woman{ } } 现在分好了,开始要让他们做两件事情: 1 让外国人在中国环游一周; 2 组织全体女性到联合国的妇联开会。 按 照第一种策略的话,让外国人在中国环游一周,就只需要让所有外国人的实例都调用一个环游的方法就可以了。但是,若要完成第二个任务,就需要先对中国的女 性调用一个组织开会的方法,再对外国女性调用一个组织开会的方法。相反,第二种策略就让第一个任务完成得比较麻烦,第二个任务就相对方便。鱼和熊掌,两者 不可兼得。 但是,如果使用接口呢?情况就大大不同。 在定义了人的接口以后: package{ public interface IPerson{ } } 再定义四个接口:男人,女人,中国人,外国人: package{ public interface IMan extends IPerson{ } package{ public interface IWoman extends IPerson{ } package{ public interface IForeigner extends IPerson{ } package{ public interface IChinese extends IPerson{ } 接下来就定义中国男人,中国女人,外国男人,外国女人。这时可以用了。 package{ public class Chinese_man implements IMan,IChinese{ } } package{ public class Chinese_woman implements IWoman,IChinese{ } } package{ public class Foreign_man implements IMan,IForeigner{ } } package{ public class Foreign_woman implements IWoan,IForeigner{ } } 同 样完成上面的两个任务,第一个,只需要调用实现接口IForeigner的就OK了,同样地,第二个也只需要调用实现接口IWoman的。当分区分 细,不再按中国人外国人分,而要按照国籍来分成200多个的时候,或者再细分至省和洲的时候,这一做法的优势就更为明显了。 总结起来,可以得知,在的继承结构不能仅用树状去表示,如上面的具有交叉继承结构的时候,就建议用接口了。但是,如果是简单的树状结构,我觉得还是用继承好些,毕竟这样的做法也有维护上的优势。
### 回答1: 使用面向对象的方法实现圆形的面积,需要创建一个Circle,该应该有一个半径属性和一个计算圆形面积的方法。在该方法中,可以使用圆的面积公式πr²来计算圆的面积。同时,需要增加一个Triangle,该应该有一个底和高属性以及一个计算三角形面积的方法。在该方法中,可以使用三角形面积公式1/2 * 底 * 高来计算三角形的面积。 在主函数中,我们需要先创建一个Circle对象,并设置半径值,然后调用计算圆形面积的方法,将结果保存到变量中。接着,我们需要再创建一个Triangle对象,并设置底和高的值,然后调用计算三角形面积的方法,将结果保存到另一个变量中。最后,我们可以将这两个变量中的值相加,得到最终的面积值。 相比于面向过程的方法,面向对象的方法有以下优点: 1. 封装性:通过将数据和相关操作封装在中,可以使代码更加清晰、简洁,并且易于维护和修改。 2. 继承性:可以通过继承已有的来创建新的,从而减少重复代码的量,并且可以使代码的结构更加清晰。 3. 多态性:通过多态性可以使代码更加灵活、可扩展,并且可以使代码更容易重用。 4. 可维护性:面向对象的代码结构更加清晰、模块化,可以使代码更易于维护和修改。 ### 回答2: 要使用面向对象的方法实现圆形的面积计算,首先需要定义一个Circle。该具有一个成员变量表示圆的半径,以及一个计算圆形面积的成员函数。 设计思路如下: 1. 定义一个Circle,包含私有成员变量radius表示圆的半径。 2. 在Circle实现一个公有成员函数calculateArea,用来计算圆的面积。该函数根据圆的半径使用数学公式计算出圆的面积,并返回结果。 3. 在主函数中,创建一个Circle对象,并调用其calculateArea函数得到圆形的面积,并打印输出。 如果增加一个三角形面积函数,可以在Circle中再增加一个公有成员函数calculateTriangleArea。该函数中可以定义三角形的三个边长为固定值,然后使用海伦公式计算三角形的面积,并返回结果。 面向对象方法相比面向过程方法的优点包括: 1. 封装性:面向对象方法利用的封装特性,将数据和相关操作封装在一起,提高代码的可维护性和复用性。 2. 继承性:通过使用继承,可以构建出一种具有层次结构的,提高代码的扩展性和灵活性。 3. 多态性:通过多态的特性,可以实现不同对象对同一消息的不同响应,提高代码的可复用性和可扩展性。 4. 抽象性:通过抽象接口的使用,可以将一些通用属性和行为进行抽象,提高代码的抽象能力和可理解性。 总的来说,面向对象方法提供了更加灵活、可复用、可维护和可扩展的编程方式,能够更好地满足软件开发的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值