最近拜讀了四人組的經典名作《設計模式 可復用面向對象軟件的基礎》一書,打算以博客的形式進行筆記與思考
書中提到了可復用面向對象設計的原則,其中的第一個原則是:
針對接口編程,而不是針對實現編程
其實在使用面向對象語言進行編程的時候,經常不經意間就會涉及書中提到的知識,但是一旦用專門的詞匯進行描述時,第一反應就是“誒?這是什么意思?看不懂啊”,只有經過反復的閱讀與思考,才意識到這是日常編程經常遇到的問題,并且得到更深的理解
舉個例子,假設有兩種品牌的輪胎,普利司通(Bridgestone)和米其林(Michelin),而輪胎的共同特性都是會轉(roll)。那么我們可以得到兩個類:
class Bridgestone { public void roll() { System.out.print("Bridgestone is rolling."); }}class Michelin { public void roll() { System.out.print("Michelin is rolling"); }}
對于一輛裝了普利司通輪胎的汽車(Car),汽車的轉動(roll)就是輪胎的轉動:
class Car { public void roll(Bridgestone tire) { tire.roll(); }}
那如果我裝了米其林的輪胎呢?
Car car = new Car();Michelin tire = new Mechilin();car.roll(tire);
顯而易見,程序將會出錯。這就是面向實現編程,變量是指向特定類的實例的。
這種強烈的依賴關系將會大大地抑制編程的靈活性和可復用性。
如果將這兩種輪胎的共同特性提取出來,在轉動輪胎的時候,只關注“是輪胎”本身,而不去了解“是什么品牌的輪胎”,問題就迎刃而解了
interface Tire { public void roll();}class Bridgestone implements Tire { public void roll() { System.out.print("Bridgestone is rolling."); }}class Michelin implements Tire { public void roll() { System.out.print("Michelin is rolling"); }}
接口Tire定義了“轉動”這個接口,但把實現延遲到了子類中
class Car { public void roll(Tire tire) { tire.roll(); }}Car car = new Car();BridgeStone tire1 = new Bridgestone();Michelin tire2 = new Mechilin();car.roll(tire1);car.roll(tire2);
汽車在轉動時,并不關注“是什么品牌的輪胎“,只關注”是輪胎“,想怎么轉就怎么轉
從這個例子我們可以看出,面向接口編程的特性:
客戶無須知道他們使用對象的特定類型,只須對象有客戶所期望的接口
客戶使用汽車轉動輪胎時,無須知道輪胎的特定類型(品牌,對應的子類),只要輪胎有客戶所期望的接口(roll)就行了
客戶無須知道他們使用的對象是用什么類來實現的,他們只須知道定義接口的抽象類
客戶使用Car的roll方法調用輪胎對應的方法時,不需要知道這個輪胎實例是用什么子類實現的,他只需要知道定義轉動方法的抽象類(JAVA中的interface)的內容就行了
面向接口的編程方式是面向對象設計的一個原則,使用這種編程思想,我們可以容易地寫出具有可復用性的代碼,這對于代碼的理解和維護具有很大的幫助