Java設計模式(3:介面隔離原則和迪米特法則詳解)
一、介面隔離原則
使用多個介面,而不使用單一的介面,客戶端不應該依賴它不需要的介面。盡量的細化介面的職責,降低類的耦合度。
我們先來看一個例子:
小明家附近新開了一家動物園,裡面有老虎、鳥兒、長頸鹿…..周末在逛動物園的時候,小明突發奇想,想用一種方式記錄一下這些動物的習性,於是他將老虎和鳥兒的習性結合了一下,寫了下面這段程式碼:
動物行為
// 動物行為
public interface Animal {
// 吃
public void eat();
// 游泳
public void swim();
// 飛
public void fly();
}
老虎Tiger
// 老虎
public class Tiger implements Animal {
@Override
public void eat() {
System.out.println("老虎在吃雞肉.....");
}
@Override
public void swim() {
System.out.println("老虎在游泳.....");
}
@Override
public void fly() {
System.out.println("老虎不能飛.....");
}
}
小鳥Brid
// 小鳥
public class Brid implements Animal {
@Override
public void eat() {
System.out.println("小鳥在吃蟲子.....");
}
@Override
public void swim() {
System.out.println("小鳥不會游泳.....");
}
@Override
public void fly() {
System.out.println("小鳥正在飛.....");
}
}
寫完上面的三段程式碼後,小明發現了問題:在
Animal
介面的三個方法中,Tiger
是不會飛的,所以fly()
方法對於Tiger
是沒有用的;Bird
是不會游泳的,所以swim()
方法對於Bird
是沒有用的。這樣一來,Brid
類和Tiger
類都會空置一個方法,對於程式碼的結構設計來說不太合理。於是,他劃掉了上面的三段程式碼,仔細思索了一會兒,寫出了下面這幾段程式碼:
// 游泳
public interface ISwim {
public void swim();
}
// 吃
public interface IEat {
public void eat();
}
// 飛
public interface IFly {
public void fly();
}
小鳥Bird
// 小鳥
public class Brid implements IEat,IFly {
@Override
public void eat() {
System.out.println("小鳥在吃蟲子.....");
}
@Override
public void fly() {
System.out.println("小鳥正在飛.....");
}
}
老虎Tiger
// 老虎
public class Tiger implements IEat,ISwim {
@Override
public void eat() {
System.out.println("老虎在吃雞肉.....");
}
@Override
public void swim() {
System.out.println("老虎在游泳.....");
}
}
這樣來看,將
eat
、swim
、fly
三種方法拆分開來,分別放在三個不同的介面里,這樣動物擁有哪幾種習性就實現哪幾個介面,不會再用空置的方法存在,這樣看起來也簡潔明了,來看看類圖:
二、迪米特法則
又被成為最少知道原則,指的是一個對象應該對其他對象保持最少的了解。一個實體類應當盡量少地和其他實體之間發生相互作用,使得系統模組相互獨立。形象來說就是:只和朋友交流,不和陌生人說話。
迪米特法則認為,一個對象或方法,它只能夠調用以下對象:
- 該對象本身
- 作為參數傳進來的對象
- 在方法內創建的對象
我們先來模擬一個超市購物的場景:顧客Customer
到收銀台結賬,收銀員PaperBoy
負責收錢。
顧客的錢包Wallet
// 錢包
public class Wallet {
// 錢包里裝的錢
private Float value;
// 構造器
public Wallet(Float value) {
this.value = value;
}
// 獲得錢包里的錢的金額
public Float getMoney(){
return this.value;
}
// 付賬時 減錢
public void reduceMoney(Float money){
this.value -= money;
}
}
顧客Customer
// 顧客
public class Customer {
private Wallet wallet = new Wallet(50f);
public Wallet getWallet() {
return wallet;
}
}
收銀員PaperBoy
// 收銀員
public class PaperBoy {
// 收銀員收錢
public void charge(Customer customer,Float money){
Wallet wallet = customer.getWallet();
if (wallet.getMoney() >= money){
System.out.println("顧客付賬:" + money +"元");
// 減去 應付的錢
wallet.reduceMoney(money);
System.out.println("錢包里還剩:"+wallet.getMoney()+"元");
} else {
System.out.println("錢包里的金額不夠......");
}
}
}
測試、運行
// 測試
public static void main(String[] args) {
PaperBoy paperBoy = new PaperBoy();
Customer customer = new Customer();
paperBoy.charge(customer,20f);
}
從測試程式碼和運行的結果來看,好像並沒有什麼問題。讓我們來看一下類圖:
從類圖中我們發現:PaperBoy
類與Wallet
類有著千絲萬縷的關係,顧客(Customer
)的錢包(Wallet
)好像並不是自己來控制的,而是由收銀員(PaperBoy
)來決定的,就連錢包(Wallet
)裡面的錢夠不夠也是由收銀員(PaperBoy
)來判斷的;相當於顧客(Customer
)將自己的錢包(Wallet
)暴露給了收銀(PaperBoy
),這樣來看,問題就很嚴重了,顧客(Customer
)的隱私受到了侵犯,說大點就是民事糾紛,是可以上法庭的,可以通過法律追究責任的。所以我們思考良久,將上述程式碼改成下面這般:
錢包Wallet
類不變,顧客Customer
去掉給出錢包的getWallet()
方法,增加付錢的pay()
方法:
// 顧客
public class Customer {
private Wallet wallet = new Wallet(50f);
// 顧客自己付錢
public void pay(Float money){
if (wallet.getMoney() >= money){
System.out.println("顧客付賬:" + money +"元");
// 減去 應付的錢
wallet.reduceMoney(money);
System.out.println("錢包里還剩:"+wallet.getMoney()+"元");
} else {
System.out.println("錢包里的金額不夠......");
}
}
}
收銀員PaperBoy
類中的charge()
方法中的程式碼刪除原有的邏輯,改為調用顧客Customer
類中的付錢pay()
方法:
// 收銀員
public class PaperBoy {
// 收銀員收錢
public void charge(Customer customer,Float money){
customer.pay(money);
}
}
測試程式碼不變,我們再來看看類圖:
從類的結構圖來看:收銀員PaperBoy
只和顧客Customer
有聯繫,錢包Wallet
只和顧客Customer
有聯繫。再此情況下,如果把錢包Wallet
也當作一個人來看的話,這個就是如下的關係:
- 顧客
Customer
和錢包Wallet
是朋友 - 顧客
Customer
和收銀員PaperBoy
是朋友 - 錢包
Wallet
和收銀員PaperBoy
是陌生人
這個就符合我們所說的迪米特法則中的核心:只和朋友交流,不和陌生人說話。