iT邦幫忙

1

設計模式:策略模式

  • 分享至 

  • xImage
  •  

在上一個章節有提到設計模式的原則,這一章主要會提到以下三點:

1.找出程式會變的東西,把其和不會變的部分分開。

2.針對介面寫程式,而不是針對實作寫程式。

3.多用組合,少用繼承。

假設我們正在開發遊戲一個遊戲,並且以物件導向的方式創造了一個角色:鳥(Bird),讓其他種鳥的角色可以繼承:
https://ithelp.ithome.com.tw/upload/images/20230124/20157184SvX920qidZ.png

此時,為了增加角色功能的豐富程度,我們需要添加了飛行功能(fly),所以把飛行功能增加到鳥的類別上,讓繼承鳥類別的角色都可以一併繼承這個功能。

https://ithelp.ithome.com.tw/upload/images/20230124/201571843fNAA6UHD0.png

過了一段時間,我們發現了一個問題:當我們創建了新角色鴕鳥(ostrich)的時候,它竟然會飛!這似乎並不符合常理。
https://ithelp.ithome.com.tw/upload/images/20230124/20157184IHg6KvGC44.png

所以為了修改這個錯誤問題,把鴕鳥的飛行功能覆寫(override)掉,應該就解決了...
https://ithelp.ithome.com.tw/upload/images/20230124/20157184TS4KH5xLxu.png

過了一段時間,當新角色玩具鳥(ToyBird)出現時又遇到相同的問題,它既不會飛,也不會叫,所以再次覆寫飛與叫的方法:
https://ithelp.ithome.com.tw/upload/images/20230124/20157184sZEmQbMYhJ.png

此時我們覺得,每次建立一個新角色,就要重新檢查繼承而來的方法是否合理,並且視情況覆寫,這個做法有點糟,
這時候我們想到,可以把飛(fly)跟叫(Scream)拿出來,變成介面,讓會飛或會叫的鳥去實作那個介面就好,不會飛或叫的角色就可以不用實作。
https://ithelp.ithome.com.tw/upload/images/20230124/20157184EDiSotgc1c.png

沒想到,過了一陣子,我們發現這樣的做法,會讓我在創造新的角色時,有可能需要寫出跟其他角色重複的程式碼:因為很多角色的飛行模式是一樣的!更糟的是,一旦我在遊戲內修改一種飛行模式的定義,我就必須確認所有跟這個飛行模式相關的類別並修改他們的程式碼。

怎麼辦?這個時候我們需要從第一個設計模式原則下手:

1.找出程式會變的東西,把其和不會變的部分分開。
把會變的部分封裝起來,之後如果需要進行修改,就針對要變的部分進行修改,使其不會影響其他部分。

從上面的案例我們可以看到,會變的部分就是兩種行為:飛與叫,因此,我們需要建立兩組類別,把他跟原來鳥的類別分開。每一組類別都去實作不同的行為。

https://ithelp.ithome.com.tw/upload/images/20230124/20157184uvjLK7Aw7v.png

https://ithelp.ithome.com.tw/upload/images/20230124/20157184X8WSu300M7.png

接著,我們可以在角色初始化的時候,指派這些行為給不同種類的鳥,甚至可以建立SetBehavior的方法,來在程式執行的期間,可以動態的調整這些角色的行為。

https://ithelp.ithome.com.tw/upload/images/20230124/20157184dy07fMJgDd.png

public class ToyBird : Bird
{
  public ToyBird()
 {
   //不會飛也不會叫
   FlyBehavior = new FlyWithNothing();
   ScreamBehavior = new MuteScream();
 }
}
//傳入新的飛行行為,替換舊有行為
public void SetFlyBehavior(FlyBehavior flybehavior)
{
 FlyBehavior = flybehavior;
}

//傳入新的叫法,替換舊有行為
public void SetScreamBehavior(ScreamBehavior screamBehavior)
{
  ScreamBehavior = screamBehavior;
}

這個做法與之前的作法差別在於:之前的行為都是為了鳥這個超類別,或是鳥的子類別去量身打造的實作方法,因此這些行為都依賴一個實作,導致需要改變行為的時候,只能透過修改程式來達成,且會隨著類別擴大而造成維護上的困難。

在新的設計中,鳥的子類別會使用介面所代表的兩種行為。而這些行為的實作,是單純為了實作飛(fly)跟叫(Scream)的具體行為,也就不會被限制在鳥的子類別裡面。

而這樣的設計,就符合了設計原則的第二點:
2.針對介面寫程式,而不是針對實作寫程式。
https://ithelp.ithome.com.tw/upload/images/20230124/20157184voqJ5FjE8Y.png

此外,把類別組合起來的方式,就是組合,用組合建立起的系統可以將行為進行封裝,並且可以在執行起改變行為,我們可以從此看到第三種原則:

3.多用組合,少用繼承。

以上就是策略模式的運用,策略模式的定義就是去封裝一系列的演算法(可以把那些實作的行為視為演算法),並且在不影響用戶端的情境下替換並改變演算法。

Reference:
深入淺出設計模式-設計模式入門, 2/e (Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software, 2/e)


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
greenriver
iT邦研究生 4 級 ‧ 2023-01-30 09:18:53

謝謝
你是設計模型觀念,寫得最清楚的
(有圖有差XD)

henianlin iT邦新手 5 級 ‧ 2023-02-03 06:26:04 檢舉

謝謝XD PO文的難度在於 畫圖要話很久 簡短寫又只有自己看得懂

我要留言

立即登入留言