具体可以去这个空间淘宝:
http://user.qzone.qq.com/151355135里面有不少 Java 类的文章
今天实验室师兄提了一个有趣的问题“面向对象编程中为什么有了继承功能还要有接口功能?”,看似简单但还是很有深度的问题,虽然知道些答案但终究是没有完全答全,摆渡一下,整理答案如下,大家一起学习学习
类继承(extends)和实现接口(implements)是完全不同的两回事!
比如有两个基类 Base1 和 Base2,有两个接口 Intf1 和 Intf2,下面需要定义一个 MyClass 来实现一定的功能。
如果是『MyClass extends Base1, Base2』(当然 Java 并不支持多继承,现在姑且这么说),那么在 MyClass 里不需要再写任何代码,它就已经具有了两个基类的原有功能;
而如果是『MyClass implements Intf1, Intf2』,那么,在 MyClass 里必须按照两个接口的要求实现所有的方法,MyClass 才能用。
可以看出,两种做法是完全不同的。当然,仅从 Caller 程序员的角度看,用“继承”的时候,通过 MyClass 可以调用两个基类所声明的方法,用“实现”的时候,通过 MyClass 可以调用两个接口所声明的方法,好像有点差不多,所以我说“似是”;但从 Callee 程序员的角度看,两者在“面向对象”的世界里所扮演的角色是完全不同的,“继承”是用来实现“代码重用”,而“接口”更多体现的是“设计重用”。
接口作用主要是降低系统的耦合性.
如果你的系统在大多数情况下面依赖于继承的话,会导致整个系统的类体系结构非常的庞大,对于以后的需求变化的改动将是牵一发而动全身的.因为继承是高耦合的,子类知道并复用父类中的方法和属性.这种复用也叫做白箱复用.
而接口中只有方法的声明,也就是说接口形成了某种契约.只要良好的定义不同的组件之间的接口,就能够将不同的组件交给不同的开发团队开发,而另一个开发团队并不用去关心其他的开发团队是如何具体实现此接口中的方法的声明,此即黑箱复用,这样可以降低系统中组件,以及系统及系统之间的耦合性.更好的将"变化"进行有效的隔离.
接口主要是针对“实现”来说的,打个比方,接口就象“制定的标准”,而实现呢,就是按这个标准生产的产品。
比如,你是汽车引擎的标准制定者,你只需要规定尺寸,尾气的排放量等等指标,而不用管生产的过程。你这个标准对2方面有用,一是引擎厂商,二是汽车制造商。引擎厂商按你的标准生产引擎,而汽车制造商按你的标准给引擎留位置。
如果这个世界上只有一个引擎厂商,只生产一种引擎,那么你这个标准制定了也是白制定,没用的~。可是世界总是没有那么理想,只要有钱赚,一堆厂家会来生产引擎,这时候你的标准的作用就凸现出来了!对于家用型汽车生产商,它们可以选择价格便宜、功率较小的。而赛车生产商,它们可以选择功率强劲,油耗大的。对于同一款产品,如果汽车生产商黑,它还可以在量产的中途改用更差的引擎,而其他地方不用改。
换到程序的领域也是如此,你定义好一个接口,那么对2方面有用,一是实现这个接口的程序员,另一方面是需要用这个实现的人。
如果你保证用到这个实现的地方,永远不会被修改(不会象黑心厂商那样),那么这个接口就没什么用,还没有你直接用实现类简单。
可是世界总是没这么理想,特别是程序的世界里。
今天,你的实现是针对ORACLE的,你的代码中用到了跟ORACLE特性相关的内容。明天,你的客户可能跟你说:SORRY,ORACLE太贵了,你能不能帮我做成MYSQL的版本? 为了满足他的需求,你有2种选择:
1、在以前的实现类上直接做修改。
2、新做一个实现类,在用到这个实现类的地方改代码为新的实现类。
如果你只有一个客户,也保证他不会再要求改回用ORACLE,第一种选择更为简单。可是如果不满足以上2个条件,那么第2种选择更为合适。 现在我们以具体的代码来示范。
(1)假设不用接口,你在用到这个实现的时候,你会这么写代码:
OracleImpl impl=new OracleImpl();
如果你把这个实现给多个方法用,那么方法的定义可能是这样:
void useImpl(OracleImpl impl){}
那么当你需要把OracleImpl改成MysqlImpl的时候,你要改3个地方。
(2)使用接口,你会这么写代码:
Interface impl=new OracleImpl();
如果你把这个实现给多个方法用,那么方法的定义可能是这样:
void useImpl(Interface impl){}
那么当你需要把OracleImpl改成MysqlImpl的时候,你只需要改1个地方。假设用到的地方很多,是不是比(1)强很多?但这还是不够!因为如果你有多个实例,那么你需要多个new OracleImpl();,这也需要你改多个地方。
(3)使用工厂模式
工厂模式就是把实例的产生的代码集中到一个类中,用这个类来产生实例。假设你定义的静态方法:
public class ImplFactory{
public static Interface getImpl(){
return new OracleImpl();
}
}
在用到的地方,你会这么写代码:
Interface impl=ImplFactory.getImpl();
这样,当你改成MysqlImpl的时候,你是不是只用改工厂类的一个地方?是的!可是这还不够,因为你毕竟还要改代码,有没有不改代码的方法呢?有!
(4) 比如我们把需要产生的实现类的类名放到一个配置文件中,假定这样写:
Interface=OracleImpl
那么我们工厂类可以这样:
public class ImplFactory{
public static Interface getImpl(){
String implClassName=\\此处略去从文件读的代码
return (Interface)(Class.forName(implClassName).newInstance());
}
}
当你需要改实现类为MysqlImpl的时候,你一行代码都不用改!只改配置文件就好了。
======================
这就是接口的魅力!
======================