面向过程编程(Procedure Oriented
、简称PO
) 和 面向对象编程(Object Oriented
、简称OO
) 咱们必定听过,然而实际企业级开发里受用更多的一种编程思想那就是:面向接口编程(Interface-Oriented
)!java
接口这个概念咱们必定不陌生,实际生活中最多见的例子就是:插座!程序员
咱们只须要事先定义好插座的接口标准,各大插座厂商只要按这个接口标准生产,管你什么牌子、内部什么电路结构,这些均和用户无关,用户拿来就能够用;并且即便插座坏了,只要换一个符合接口标准的新插座,一切照样工做!编程
同理,实际代码设计也是这样!bash
咱们在设计一个软件的代码架构时,咱们都但愿事先约定好各个功能的接口(即:约定好接口签名和方法),实际开发时咱们只须要实现这个接口就能完成具体的功能!后续即便项目变化、功能升级,程序员只须要按照接口约定从新实现一下,就能够达到系统升级和扩展的目的!架构
正好,Java中天生就有interface
这个语法,这简直是为面向接口编程而生的!框架
因此接下来落实到代码上,举个通俗一点的小例子唠一唠,实际业务代码虽然比这个复杂,但原理是如出一辙的。ide
假如哪一天程序羊真发达了,一口豪气买了两辆豪车,一辆五菱宏光、一辆飞度、而且还专门聘请了一位驾驶员来帮助驾驶。函数
两辆豪车在此:工具
public class Wuling {
public void drive() {
System.out.println("驾驶五菱宏光汽车");
}
}
public class Fit {
public void drive() {
System.out.println("驾驶飞度汽车");
}
}
复制代码
驾驶员定义在此:测试
驾驶员定义了两个drive()
方法,分别用来驾驶两辆车:
public class Driver {
public void drive( Wuling wuling ) {
wuling.drive(); // 驾驶五菱宏光的方法
}
public void drive( Fit fit ) {
fit.drive(); // 驾驶飞度的方法
}
// 用于测试功能的 main()函数
public static void main( String[] args ) {
// 实例化两辆新车
Wuling wuling = new Wuling();
Fit fit = new Fit();
// 实例化驾驶员
Driver driver = new Driver();
driver.drive( wuling ); // 帮我开五菱宏光
driver.drive( fit ); // 帮我开飞度
}
}
复制代码
这暂且看起来没问题!日子过得很融洽。
但后来过了段时间,程序羊又变得发达了一点,此次他又豪气地买了一辆新款奥拓(Alto)!
但是现有的驾驶员类Driver
的两个drive()
方法里都开不了这辆新买的奥拓该怎么办呢?
这时候,我想应该没有谁会专门再去往Driver
类中添加一个新的drive()
方法来达到目的吧?毕竟谁也不知道之后他还会不会买新车!
这时候若是我但愿我聘请的这位驾驶员对于全部车型都能驾驭,该怎么办呢?
很容易想到,咱们应该作一层抽象。毕竟不论是奥拓仍是奥迪,它们都是汽车,所以咱们定义一个父类叫作汽车类Car
,里面只声明一个通用的drive()
方法,具体怎么开先不用管:
// 抽象的汽车类Car,表明全部汽车
public class Car {
void drive() { } // 通用的汽车驾驶方法
}
复制代码
这时,只要我新买的奥拓符合Car
定义的驾驶标准便可被个人驾驶员驾驶,因此只须要新的奥拓来继承一下Car
类便可:
public class Alto extends Car {
public void drive() {
System.out.println("驾驶奥拓汽车");
}
}
复制代码
同理,只须要个人驾驶员具有通用汽车Car
的驾驶能力,那驾驶全部的汽车都不是问题,所以Drvier
类的drive()
方法只要传入的参数是父类,那就具有了通用性:
public class Driver {
public void drive( Car car ) { // 方法参数使用父类来替代
car.drive();
}
public static void main( String[] args ) {
Alto alto = new Alto();
Driver driver = new Driver();
driver.drive( alto );
}
}
复制代码
问题暂且解决了!
可是再后来,程序羊他好像又更发达了一些,连车都不想坐了,想买一头驴(Donkey)让司机骑着带他出行!
很明显,原先适用于汽车的drive()
方法确定是不适合骑驴的!但咱们但愿聘请的这位驾驶员既会开汽车,又会骑驴怎么办呢?
害!咱们干脆直接定义一个叫作交通工具(TrafficTools
)的通用接口吧!里面包含一个通用的交通工具使用方法,管你是驾驶汽车,仍是骑驴骑马,具体技能怎么实现先无论:
// 通用的交通工具接口定义
public interface TrafficTools {
void drive(); // 通用的交通工具使用方法
}
复制代码
有了这个接口约定,接下来就好办了。咱们让全部的Car
、或者驴、马等,都来实现这个接口:
public class Car implements TrafficTools {
@Override
public void drive() { }
}
public class Wuling extends Car {
public void drive() {
System.out.println("驾驶五菱宏光汽车");
}
}
public class Fit extends Car {
public void drive() {
System.out.println("驾驶飞度汽车");
}
}
public class Alto extends Car {
public void drive() {
System.out.println("驾驶奥拓汽车");
}
}
public class Donkey implements TrafficTools {
@Override
public void drive() {
System.out.println("骑一头驴");
}
}
复制代码
这个时候只要咱们的驾驶员师傅也面向接口编程,就没有任何问题:
public class Driver {
// 方法参数面向接口编程
public void drive( TrafficTools trafficTools ) {
trafficTools.drive();
}
public static void main( String[] args ) {
Driver driver = new Driver();
driver.drive( new Wuling() ); // 开五菱
driver.drive( new Fit() ); // 开飞度
driver.drive( new Alto() ); // 开奥拓
driver.drive( new Donkey() ); // 骑一头驴
}
}
复制代码
很明显,代码彻底解耦了!这就是接口带来的便利。
面向接口编程的优势远不止上面这种代码解耦的场景,在实际企业开发里,利用接口思想对已有代码进行灵活扩展也特别常见。
再举一个例子:假设程序羊有一个很是豪气的朋友,叫:程序牛,他们家出行可不坐车,全靠私人飞机出行:
// 通用的飞机飞行接口
public interface Plane {
void fly();
}
// 程序牛的专用机长,受过专业训练(即:实现了通用飞行接口)
public class PlaneDriver implements Plane {
@Override
public void fly() {
System.out.println("专业的飞行员操控飞机");
}
}
// 出门旅行
public class Travel {
// 此处函数参数也是面向接口编程!!!
public void fly( Plane plane ) {
plane.fly();
}
public static void main( String[] args ) {
Travel travel = new Travel(); // 开启一段旅行
PlaneDriver planeDriver = new PlaneDriver(); // 聘请一个机长
travel.fly( planeDriver ); // 由专业机长开飞机愉快的出去旅行
}
}
复制代码
可是忽然有一天,他们家聘请的机长跳槽了,这时候程序牛一家就没法出行了,毕竟飞机不会驾驶。
因而他跑来问我借司机,想让个人驾驶员来帮他驾驶飞机出去旅行。
我一看,因为他们的代码面向的是接口,我就确定地答应了他!
这时候对我这边的扩展来讲就很是容易了,我只须要安排个人驾驶员去培训一下飞行技能就OK了(实现一个方法就行):
// 让个人驾驶员去培训一下飞行技能(即:去实现通用飞行接口)
public class Driver implements Plane {
public void drive( TrafficTools trafficTools ) {
trafficTools.drive();
}
// 实现了fly()方法,这下个人驾驶员也具有操控飞机的能力了!
@Override
public void fly() {
System.out.println("普通驾驶员操控飞机");
}
}
复制代码
这时候个人驾驶员Driver
类就能够直接服务于他们一家的出行了:
public class Travel {
public void fly( Plane plane ) {
plane.fly();
}
public static void main( String[] args ) {
Travel travel = new Travel();
// 专业飞行员操控飞机
PlaneDriver planeDriver = new PlaneDriver();
travel.fly( planeDriver );
// 普通驾驶员操控飞机
Driver driver = new Driver();
travel.fly( driver );
}
}
复制代码
看到没,这一改造过程当中,咱们只增长了代码,却并无修改任何已有代码,就完成了代码扩展的任务,很是符合开闭原则!
实际开发中,咱们就暂且不说诸如Spring
这种框架内部会大量使用接口,并对外提供使用,就连咱们本身平时写业务代码,咱们也习惯于在Service
层使用接口来进行一层隔离:
这种接口定义和具体实现逻辑的分开,很是有利于后续扩展和维护!
面向接口编程开发,对代码架构的解耦和扩展确实颇有好处,这种编码思想也值得平时开发结合实践反复理解和回味!