JavaRush /Java Blog /Random-TW /設計模式“策略”

設計模式“策略”

在 Random-TW 群組發布
你好!在前面的講座中,我們已經接觸過「設計模式」這樣一個概念。如果您忘記了,讓我們提醒您:該術語表示程式設計中常見問題的某種標準解決方案。 設計模式“策略” - 1在 JavaRush,我們經常說幾乎任何問題的答案都可以透過 Google 搜尋。因此,有人可能已經成功解決了與您類似的問題。因此,模式是針對最常見問題的經過時間考驗和實踐檢驗的解決方案或解決問題情況的方法。這些正是您在任何情況下都不需要自己發明的“自行車”,但您需要知道如何以及何時應用它們:) 模式的另一個任務是將架構引入單一標準。閱讀別人的程式碼並不是一件容易的事!每個人寫的都不一樣,因為同樣的問題可以有許多解決方式。但是模式的使用可以讓不同的程式設計師無需深入研究每一行程式碼就可以理解程式的邏輯(即使他們是第一次看到它!)今天我們將看看一種最常見的模式,稱為「策略」。 設計模式“策略”- 2假設我們正在編寫一個主動使用 Car 物件的程式。在這種情況下,我們的程式到底做什麼並不特別重要。為此,我們創建了一個具有一個父類別Auto和三個子類別的繼承系統:SedanTruckF1Car
public class Auto {

   public void gas() {
       System.out.println("Едем вперед");
   }

   public void stop() {

       System.out.println("Тормозим!");
   }
}

public class Sedan extends Auto {
}

public class Truck extends Auto {
}

public class F1Car extends Auto {
}
所有三個子類別都從父類別繼承了兩個標準方法 -gas()我們stop() 的程式非常簡單:汽車只能向前行駛和煞車。繼續我們的工作,我們決定為汽車添加一種新方法 - fill()(加油)。讓我們將它加入父類別Auto
public class Auto {

   public void gas() {
       System.out.println("Едем вперед");
   }

   public void stop() {

       System.out.println("Тормозим!");
   }

   public void fill() {
       System.out.println("Заправить бензин!");
   }
}
這麼簡單的狀況,似乎也會出問題?嗯,事實上,他們已經出現了… 設計模式“策略”- 3
public class ChildrenBuggies extends Auto {

   public void fill() {

       //хм... Это детский багги, его не надо заправлять :/
   }
}
我們的程式中出現了一輛不符合一般概念的汽車—兒童車。它可能是踏板驅動的或無線電控制的,但有一件事是肯定的——沒有地方可以在裡面加汽油。我們的繼承方案導致我們放棄了通用方法,甚至給不需要它們的類別。遇到這樣的情況我們該怎麼辦呢?好吧,例如,您可以重寫fill()類別中的方法ChildrenBuggies,這樣當您嘗試為越野車加油時,就不會發生任何事情:
public class ChildrenBuggies extends Auto {

   @Override
   public void fill() {
       System.out.println("Игрушечную машину нельзя заправить!");
   }
}
但這個解決方案很難說是成功的,至少因為程式碼重複。例如,大多數類別將使用父類別中的方法,但其他類別將被迫覆蓋它。如果我們有 15 個類,並且在 5-6 個類別中我們被迫重寫行為,那麼程式碼重複將變得相當廣泛。也許接口可以幫助我們?例如,這個:
public interface Fillable {

   public void fill();
}
我們將Fillable使用一種方法來建立一個介面fill()。因此,那些需要加油的汽車將實現這個接口,但其他汽車(例如我們的越野車)不會。但這個選擇也不適合我們。我們的類層次結構將來可能會成長到非常大的數量(想想世界上有多少種不同類型的汽車)。我們放棄了之前的繼承選項,因為我們不想覆蓋fill(). 這裡我們要在每堂課中貫徹落實!如果我們有 50 個怎麼辦?如果對我們的程式進行頻繁的更改(在實際程式中幾乎總是會發生!),我們將不得不在所有 50 個類別之間徘徊並手動更改每個類別的行為。那我們最終該做什麼呢?為了解決我們的問題,讓我們選擇一條不同的道路。也就是說,讓我們將類別的行為與類別本身分開。這是什麼意思?如您所知,任何物件都有狀態(一組資料)和行為(一組方法)。我們的機器類別的行為由三個方法組成 - gas()stop()fill()。前兩種方法都很好。但我們將把第三個方法移到類別之外Auto。這將是行為與類別的分離(更準確地說,我們僅分離部分行為 - 前兩個方法保持不變)。我們應該把我們的方法移到哪裡fill()?沒有什麼立即浮現在腦海中:/他似乎完全處於他的位置。我們將把它移動到一個單獨的介面 - FillStrategy
public interface FillStrategy {

   public void fill();
}
為什麼我們需要這個介面?這很簡單。現在我們可以建立幾個實作該介面的類別:
public class HybridFillStrategy implements FillStrategy {

   @Override
   public void fill() {
       System.out.println("Заправляем бензином or электричеством на выбор!");
   }
}

public class F1PitstopStrategy implements FillStrategy {

   @Override
   public void fill() {
       System.out.println("Заправляем бензин только после всех остальных procedures пит-стопа!");
   }
}

public class StandartFillStrategy implements FillStrategy {
   @Override
   public void fill() {
       System.out.println("Просто заправляем бензин!");
   }
}
我們為傳統汽車、混合動力汽車和一級方程式汽車制定了三種行為策略。每個策略都實作單獨的加油演算法。在我們的例子中,這只是輸出到控制台,但方法內部可能有一些複雜的邏輯。接下來我們該做什麼呢?
public class Auto {

   FillStrategy fillStrategy;

   public void fill() {
       fillStrategy.fill();
   }

   public void gas() {
       System.out.println("Едем вперед");
   }

   public void stop() {
       System.out.println("Тормозим!");
   }

}
我們使用我們的介面FillStrategy作為父類別中的欄位Auto。請注意:我們沒有指定具體的實現,而是使用介面。我們需要FillStrategy在子汽車類別中具體實作該介面:
public class F1Car extends Auto {

   public F1Car() {
       this.fillStrategy = new F1PitstopStrategy();
   }
}

public class HybridAuto extends Auto {

   public HybridAuto() {
       this.fillStrategy = new HybridFillStrategy();
   }
}

public class Sedan extends Auto {

   public Sedan() {
       this.fillStrategy = new StandartFillStrategy();
   }
}
讓我們看看我們得到了什麼:
public class Main {

   public static void main(String[] args) {

       Auto sedan = new Sedan();
       Auto hybrid = new HybridAuto();
       Auto f1car = new F1Car();

       sedan.fill();
       hybrid.fill();
       f1car.fill();
   }
}
控制台輸出:

Просто заправляем бензин!
Заправляем бензином or электричеством на выбор!
Заправляем бензин только после всех остальных procedures пит-стопа!
太好了,加油過程正常進行!順便說一句,沒有什麼可以阻止我們在建構函式中使用策略作為參數!例如,像這樣:
public class Auto {

   private FillStrategy fillStrategy;

   public Auto(FillStrategy fillStrategy) {
       this.fillStrategy = fillStrategy;
   }

   public void fill() {
       this.fillStrategy.fill();
   }

   public void gas() {
       System.out.println("Едем вперед");
   }

   public void stop() {
       System.out.println("Тормозим!");
   }
}

public class Sedan extends Auto {

   public Sedan() {
       super(new StandartFillStrategy());
   }
}



public class HybridAuto extends Auto {

   public HybridAuto() {
       super(new HybridFillStrategy());
   }
}

public class F1Car extends Auto {

   public F1Car() {
       super(new F1PitstopStrategy());
   }
}
讓我們運行我們的方法main()(它保持不變)並得到相同的結果!控制台輸出:

Просто заправляем бензин!
Заправляем бензином or электричеством на выбор!
Заправляем бензин только после всех остальных procedures пит-стопа!
策略模式定義了一系列演算法,封裝了它們中的每一個,並確保它們是可互換的。它允許您修改演算法,無論它們在客戶端的使用如何(這個定義取自《探索設計模式》一書,在我看來非常成功)。 設計模式“策略”- 4我們將我們感興趣的演算法系列(加油車類型)隔離到具有多種實作的單獨介面中。我們將它們與汽車的本質分開。因此,現在,如果我們需要對這個或那個加油過程進行任何更改,這不會以任何方式影響我們的汽車類別。至於可互換性,為了實現它,我們只需要在我們的類別中添加一個 setter 方法Auto
public class Auto {

   FillStrategy fillStrategy;

   public void fill() {
       fillStrategy.fill();
   }

   public void gas() {
       System.out.println("Едем вперед");
   }

   public void stop() {
       System.out.println("Тормозим!");
   }

   public void setFillStrategy(FillStrategy fillStrategy) {
       this.fillStrategy = fillStrategy;
   }
}
現在我們可以即時改變策略:
public class Main {

   public static void main(String[] args) {

       ChildrenBuggies buggies = new ChildrenBuggies();
       buggies.setFillStrategy(new StandartFillStrategy());

       buggies.fill();
   }
}
如果突然間兒童越野車開始加滿汽油,我們的程序將為這種情況做好準備:)實際上,僅此而已!您已經學習了另一種設計模式,您無疑會需要它,並且在處理實際項目時會不止一次地幫助您:) 再見!
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION