面向接口编程和面向对象编程
现今的高级语言如Java、C#等都提供了interface这一关键字用于定义接口,因为在这类语言中都可以定义抽象类。很多人便把抽象类和接口进行了等价,认为接口的作用便是提供多继承。然而当我们仔细研究Java类库中定义的接口,大都可以看到类似于Cloneable、Appendable、Closeable的接口定义。从这些接口定义的名字可以看到,接口的作用是用于表明一种能力或者说是定义一个协议。凡是实现了某个接口的类就表明具有了某种能力,能够完成指定的任务。
既然是定义某种协议或方法让子类实现,那么抽象类同样可以实现。那么何时定义抽象类何时定义接口,它们二者有什么区别。接口用来描述行为的集合,在现实中并没有对应的实体。而抽象类虽然在现实世界中也没有对应的实体,但是确描述了一个具体的实体的轮廓,子类主要是通过继承父类的轮廓和实现接口定义的行为来最大化的个性化自己行为。
以现实世界中的汽车为例子。我们知道现实中汽车都能够跑,那我们便可以定义一个跑行为规则,用来指定所有实现这个接口的类都可以承担跑这个任务。
interface IRun{
void runWithNoFire(); //空挡滑行
void runWithHighSpeed(); //高速运行
void runWithLowSpeed(); //低速运行
}
现在我们定义现实世界中存在的各种各样的汽车。单单汽车这个概率是抽象的,现实中有的是各种各样品牌的车,有的跑的快、有的价格高。每个都有自己的特性,但最终都称之为汽车。通过这些可以看出,不管你造出什么造型比较独特、价格比较昂贵的车,你都要被称之为汽车只是你自己为了区别给取了一个名字。那么通过上述描述,可以看出汽车这一概念是抽象的,但是却可以对应现实世界抽象的实体。这样,我们便定义一个汽车抽象类。
abstract class Car implements IRun{
public abstract void openCarDoor();
public abstract void closeCarDoor();
public abstract void fire();
}
现在我们定义一个现实存在、具有自己特性的实体汽车。
class BMWCar extends Car{
public void openCarDoor() {
System.out.println("BMW open door");
}
public void closeCarDoor() {
System.out.println("BMW close door");
}
public void fire() {
System.out.println("BMW fire...");
}
public void runWithNoFire() {
System.out.println("BMW run without fire");
}
public void runWithHighSpeed() {
System.out.println("BMW run with high speed");
}
public void runWithLowSpeed() {
System.out.println("BMW run with low speed");
}
}
至此,我们就构建了一个具有跑的能力且具有车的轮廓,个性鲜明的BMW车。
JDK中的面向接口&对象编程
JDK总体上使用了面向接口编程的思想,即类库向开发者提供高层的接口然后隐藏具体的实现。然后在类库升级重构时,就可以更改底层的实现而使得开发者的调用无需改变就可以适应JDK的升级。它主要就是通过提供一个接口或者抽象类,供开发者使用,然后设计者可以随意更改实现。
在JDK的NIO包中,有一抽象的Selector类。它的作用就相当一个Selector实体的轮廓,然后让底层去实现各种各样形态各异的Selector。下面是Selector的继承图
读者会发现,这里会有一个AbstractSelector类。它主要的作用是对抽象类Selector的简单功能做部分实现,减轻SelectorImpl的工作量。这样也可以减少用户自定义实现的工作量,使得用户可以直接继承AbstractSelector的部分实现。通过这些手段就可以达到抽象实体及轮廓和实现分离、行为即接口和实现分离的目的。
同时JDK中,为了达到更大的松耦合,通常通过一个工厂类来使得构建对象和使用对象相分离。这在一个对象构建相对简单、消耗资源较少构建速度较快的过程中是体现不出来具体效果的,但对于这些消耗资源的构建过程工厂类提供了巨大的便利。这使得开发者不用去关心具体使用的是那些实现及如何设置参数,不过也会失去一定的灵活性。
下面便是Selector使用工厂的类图
现在我们再看看构建Selector的过程:
Selector sl=Selector.open();
通过Selector的静态方法直接建立一个Selector。再来看看open的实现:
public static Selector open() throws IOException {
return SelectorProvider.provider().openSelector();
}
通过SelectorProvider.provider()获得一个SelectorProvider的实例,然后通过工厂的openSelector新建一个Selector,便完成了一个新建的过程,实现了解耦的目的。