为什么我们要面向接口编程

1. 到底面向??编程

面向过程编程( ProcedureOriented、简称 PO) 和 面向对象编程( ObjectOriented、简称 OO) 我们一定听过,然而实际企业级开发里受用更多的一种编程思想那就是:面向接口编程Interface-Oriented)!

接口这个概念我们一定不陌生,实际生活中最常见的例子就是:插座!

我们只需要事先定义好插座的接口标准,各大插座厂商只要按这个接口标准生产,管你什么牌子、内部什么电路结构,这些均和用户无关,用户拿来就可以用;而且即使插座坏了,只要换一个符合接口标准的新插座,一切照样工作!

同理,实际代码设计也是这样!

我们在设计一个软件的代码架构时,我们都希望事先约定好各个功能的接口(即:约定好接口签名和方法),实际开发时我们只需要实现这个接口就能完成具体的功能!后续即使项目变化、功能升级,程序员只需要按照接口约定重新实现一下,就可以达到系统升级和扩展的目的!

正好,Java中天生就有 interface这个语法,这简直是为面向接口编程而生的!

所以接下来落实到代码上,举个通俗一点的小例子唠一唠,实际业务代码虽然比这个复杂,但原理是一模一样的。

2. 实例(做梦了)

假如哪一天博主真发达了,一口豪气买了两辆豪车,一辆五菱宏光、一辆飞度、并且还专门聘请了一位驾驶员来帮助驾驶。

两辆豪车在此:

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()方法里都开不了这辆新买的奥拓该怎么办呢?

3. 代码的灵活解耦

这时候,我想应该没有谁会专门再去往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 = newAlto();
		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());// 骑一头驴
	}
}

很明显,代码完全解耦了!这就是接口带来的便利。

4. 代码的扩展性

面向接口编程的优点远不止上面这种代码解耦的场景,在实际企业开发里,利用接口思想对已有代码进行灵活扩展也特别常见。

再举一个例子:假设有一人,他们家出行可不坐车,出行全靠私人飞机出行:

// 通用的飞机飞行接口
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 );    
	}
}

看到没,这一改造过程中,我们只增加了代码,却并没有修改任何已有代码,就完成了代码扩展的任务,非常符合开闭原则

5. 实际项目

实际开发中,我们就暂且不说诸如Spring这种框架内部会大量使用接口,并对外提供使用,就连我们自己平时写业务代码,我们也习惯于在Service层使用接口来进行一层隔离:

这种接口定义和具体实现逻辑的分开,非常有利于后续扩展和维护!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值