Java介面的例項應用:致敬我的偶像——何塞·穆里尼奧

沉默王二發表於2018-12-21

文/沉默王二

文/沉默王二

曹操在《短歌行》中為杜康酒打過一個價值一億個億的廣告——“何以解憂,唯有杜康”,我替曹操感到惋惜的是他本人並不會收到這筆不菲的代言費。想一想,要是三國時期的明星人物們有這個代言意識的話,保證各家的軍費收入會多出來一個重量級的來源。

不過,酒真的能解憂嗎?我不大敢相信。李白就曾質疑過:“舉杯消愁愁更愁,抽刀斷水水更流。”我和李白持相同的觀點,酒啊,真的不容易解憂,但絕對可以增加作者莫名的寫作衝動。

我在寫本文之前就小酌了一杯,一不小心激發了我強烈的創作慾望。不過我要奉勸各位,寒冬之際,如果遇到煩心事,千萬別肆意地追求一醉方休,萬事要懂得適可而止

01 抽象類

一種比較蒼白的說法是:在Java中,通過關鍵字abstract定義的類叫做抽象類。Java是一門物件導向的語言,因此所有的物件都是通過類來描述的;但反過來,並不是所有的類都是用來描述物件的,抽象類就是其中的一種。

以下示例展示了一個簡單的抽象類:

// 個人認為,一名教練必須攻守兼備
abstract class Coach {
	public abstract void defend();

	public abstract void attack();
}
複製程式碼

在一個抽象類中,至少有一個抽象方法(通過關鍵字abstract定義的方法,並且沒有方法體,如上例中的defend()方法和attack()方法),否則就沒有必要稱之為抽象類。需要注意的是,抽象類是不能例項化的! 它需要被一個子類繼承,就像以下示例那樣。

abstract class Coach {
	public abstract void defend();

	public abstract void attack();
}


class Hesai extends Coach {

	@Override
	public void defend() {
		System.out.println("防守贏得冠軍");
	}

	@Override
	public void attack() {
		System.out.println("控球是把雙刃劍");
	}
}

public class Demo {
	public static void main(String[] args) {
		Coach moliniao = new Hesai();
		moliniao.defend();
		moliniao.attack();
	}
}
複製程式碼

我們都知道,一個好的教練,必須攻守兼備,但每個教練的進攻理念和防守理念不盡相同。因此,我在教練這個抽象類(Coach)中定義兩個抽象方法,一個進攻(attack)一個防守(defend),這兩個方法的具體實現都要由抽象類的子類確定,抽象類本身並不負責。

我們也都知道,何塞·穆里尼奧是足球界的頂級教練。他是我最愛的足球教練,沒有之一。儘管他在曼聯的失敗有他自身的原因,但我依然崇拜他,因為:“請不要說我傲慢,因為我只是實話實說,我是歐洲冠軍,因此我並非籍籍無名,而是特殊的一個!”他是固執的反控球主義者,堅信控球是把雙刃劍,防守贏得冠軍。

好了,對於抽象類我們簡單總結一下:

1、抽象類不能被例項化。 2、抽象類應該至少有一個抽象方法,否則它沒有任何意義。 3、抽象類中的抽象方法沒有方法體。 4、抽象類的子類必須給出父類中的抽象方法的具體實現,除非該子類也是抽象類。

02 介面

我們知道,有抽象方法的類被稱為抽象類,也就意味著抽象類中還能有不是抽象方法的方法。這樣的類就不能算作純粹的介面,儘管它也可以提供介面的功能——只能說抽象類是普通類與介面之間的一種中庸之道。

介面(英文:Interface),在Java中是一個抽象型別,是抽象方法的集合;介面通過關鍵字interface來定義。介面與抽象類的不同之處在於:

1、抽象類可以有方法體的方法,但介面沒有。 2、介面中的成員變數隱式為static final,但抽象類不是的。 3、一個類可以實現多個介面,但只能繼承一個抽象類。

以下示例展示了一個簡單的介面:

// 隱式的abstract
interface Coach {
	// 隱式的public
	void defend();
	void attack();
}
複製程式碼

介面是隱式抽象的,所以宣告時沒有必要使用abstract關鍵字;介面的每個方法都是隱式抽象的,所以同樣不需要使用abstract關鍵字;介面中的方法都是隱式public的。

和抽象類一樣,介面也不能直接被例項化,它需要一個類來實現它,就像以下示例展示那樣。

class Hesai implements Coach {

	@Override
	public void defend() {
		System.out.println("防守贏得冠軍");
	}

	@Override
	public void attack() {
		System.out.println("控球是把雙刃劍");
	}
}

public class Demo2 {
	public static void main(String[] args) {
		Coach moliniao = new Hesai();
		moliniao.defend();
		moliniao.attack();
	}
}
複製程式碼

實現一個介面需要用到關鍵字implements,它表示:“我這個類遵從了介面的協議,如果你想使用我,看介面就行了,具體實現不用關心。”

03 多重實現

在現實生活中,何塞·穆里尼奧不止是一名足球教練,他還是一個值得被尊重的英雄——憑藉自身的努力,他從一名籍籍無名的跟班翻譯,逐漸蛻變為一名家喻戶曉的頂級教練。

如果要在程式的世界裡體現何塞·穆里尼奧的多重角色,就可以使用介面,就像以下示例展示那樣。

package com.cmower.java_demo.nine.inf;

interface Coach {
	// 隱式的public
	void defend();
	void attack();
}

interface Hero {
	void fight();
}


class Hesai implements Coach, Hero {

	@Override
	public void defend() {
		System.out.println("防守贏得冠軍");
	}

	@Override
	public void attack() {
		System.out.println("控球是把雙刃劍");
	}

	@Override
	public void fight() {
		System.out.println("只要一息尚存,就應該戰鬥到最後");
	}
}

public class Demo2 {
	public static void defend(Coach coach) {
		coach.defend();
	}
	
	public static void fight(Hero hero) {
		hero.fight();
	}
	
	public static void main(String[] args) {
		Hesai moliniao = new Hesai();
		defend(moliniao);
		fight(moliniao);
	}
}
複製程式碼

可以看到,建立的Hesai物件可以向上轉型為Coach和Hero,然後呼叫各自介面中實現的具體方法,因為Hesai這個類同時實現了兩個介面,分別是Coach和Hero(class Hesai implements Coach, Hero,介面之間通過英文逗號隔開)。

04 介面在應用中常見的三種模式

在程式設計領域,好的設計模式能夠讓我們的程式碼事半功倍。在使用介面的時候,經常會用到三種模式,分別是策略模式、介面卡模式和工廠模式。

1)策略模式

策略模式的思想是,針對一組演算法,將每一種演算法封裝到具有共同介面的實現類中,介面的設計者可以在不影響呼叫者的情況下對演算法做出改變。示例如下:

// 介面:教練
interface Coach {
	// 方法:防守
	void defend();
}

// 何塞·穆里尼奧
class Hesai implements Coach {

	@Override
	public void defend() {
		System.out.println("防守贏得冠軍");
	}
}

// 德普·瓜迪奧拉
class Guatu implements Coach {

	@Override
	public void defend() {
		System.out.println("進攻就是最好的防守");
	}
}

public class Demo {
	// 引數為介面
	public static void defend(Coach coach) {
		coach.defend();
	}
	
	public static void main(String[] args) {
		// 為同一個方法傳遞不同的物件
		defend(new Hesai());
		defend(new Guatu());
	}
}
複製程式碼

Demo.defend()方法可以接受不同風格的Coach,並根據所傳遞的引數物件的不同而產生不同的行為,這被稱為“策略模式”。

2)介面卡模式

介面卡模式的思想是,針對呼叫者的需求對原有的介面進行轉接。生活當中最常見的介面卡就是HDMI(英語:High Definition Multimedia Interface,中文:高清多媒體介面)線,可以同時傳送音訊和視訊訊號。介面卡模式的示例如下:

interface Coach {
	void defend();
	void attack();
}

// 抽象類實現介面,並置空方法
abstract class AdapterCoach implements Coach {
	public void defend() {};
	public void attack() {};
}

// 新類繼承介面卡
class Hesai extends AdapterCoach {
	public void defend() {
		System.out.println("防守贏得冠軍");
	}
}

public class Demo {
	public static void main(String[] args) {
		Coach coach = new Hesai();
		coach.defend();
	}
}
複製程式碼

Coach介面中定義了兩個方法(defend()和attack()),如果類直接實現該介面的話,就需要對兩個方法進行實現。

如果我們只需要對其中一個方法進行實現的話,就可以使用一個抽象類作為中介軟體,即介面卡(AdapterCoach),用這個抽象類實現介面,並對抽象類中的方法置空(方法體只有一對花括號),這時候,新類就可以繞過介面,繼承抽象類,我們就可以只對需要的方法進行覆蓋,而不是介面中的所有方法。

3)工廠模式

所謂的工廠模式理解起來也不難,就是什麼工廠生產什麼,比如說寶馬工廠生產寶馬,賓士工廠生產賓士,A級學院畢業A級教練,C級學院畢業C級教練。示例如下:

// 教練
interface Coach {
	void command();
}

// 教練學院
interface CoachFactory {
	Coach createCoach();
}

// A級教練
class ACoach implements Coach {

	@Override
	public void command() {
		System.out.println("我是A級證照教練");
	}
	
}

// A級教練學院
class ACoachFactory implements CoachFactory {

	@Override
	public Coach createCoach() {
		return new ACoach();
	}
	
}

// C級教練
class CCoach implements Coach {

	@Override
	public void command() {
		System.out.println("我是C級證照教練");
	}
	
}

// C級教練學院
class CCoachFactory implements CoachFactory {

	@Override
	public Coach createCoach() {
		return new CCoach();
	}
	
}

public class Demo {
	public static void create(CoachFactory factory) {
		factory.createCoach().command();
	}
	
	public static void main(String[] args) {
		// 對於一支球隊來說,需要什麼樣的教練就去找什麼樣的學院
		// 學院會介紹球隊對應水平的教練。
		create(new ACoachFactory());
		create(new CCoachFactory());
	}
}
複製程式碼

有兩個介面,一個是Coach(教練),可以command()(指揮球隊);另外一個是CoachFactory(教練學院),能createCoach()(教出一名優秀的教練)。然後ACoach類實現Coach介面,ACoachFactory類實現CoachFactory介面;CCoach類實現Coach介面,CCoachFactory類實現CoachFactory介面。當需要A級教練時,就去找A級教練學院;當需要C級教練時,就去找C級教練學院。

依次類推,我們還可以用BCoach類實現Coach介面,BCoachFactory類實現CoachFactory介面,從而不斷地豐富教練的梯隊。

05 總結

儘管介面使得抽象更進一步,但任何抽象性都應該根據真正的需求而產生,因此恰當的原則是優先選擇類而不是介面,只有在真正需要介面的時候再重構程式碼。

相關文章