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("老虎在游泳.....");
    }
}

這樣來看,將eatswimfly三種方法拆分開來,分別放在三個不同的介面里,這樣動物擁有哪幾種習性就實現哪幾個介面,不會再用空置的方法存在,這樣看起來也簡潔明了,來看看類圖:

二、迪米特法則

又被成為最少知道原則,指的是一個對象應該對其他對象保持最少的了解。一個實體類應當盡量少地和其他實體之間發生相互作用,使得系統模組相互獨立。形象來說就是:只和朋友交流,不和陌生人說話

迪米特法則認為,一個對象或方法,它只能夠調用以下對象:

  • 該對象本身
  • 作為參數傳進來的對象
  • 在方法內創建的對象

我們先來模擬一個超市購物的場景:顧客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是陌生人

這個就符合我們所說的迪米特法則中的核心:只和朋友交流,不和陌生人說話