【設計模式】策略模式

前言


現在有個情境如下

你的面前有紅藥水跟毒藥水

依照你的等級,會有不同效果

紅藥水: 加 (等級 x 10) 的血量

毒藥水: 減少 (等級 x 15 )的血量

那如果用程式寫的話,一般會是這樣

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
Main:

private void Start()
{
Character p1 = new Character();
p1.UseItem(Character.HpItem.RedWater);
}

角色:
public class Character
{
private int hp = 1000;
public int ChacaterLevel = 1;

public enum HpItem
{
RedWater,
ToxinWater
}

public void UseItem(HpItem item)
{
switch (item)
{
case HpItem.RedWater:
hp += 10 * ChacaterLevel;
break;
case HpItem.ToxinWater:
hp += -15 * ChacaterLevel;
break;
}
}
}

這樣的確會得到想要的效果,但如果新增藥水的話,又要回到角色這邊來新增使用物品後的效果,違反了開閉原則。

然後,如果企劃要喊說改紅藥水的數值,變成每等級補50滴,那也是要修改程式碼
日後如果又改回每等級10滴,又要跑回來修改程式碼,不管怎麼樣改,都會一直修改原始代碼。
因此策略模式就跑出來解決這個問題了!


# 應用策略模式

先上一個策略模式的圖



紅藥水跟毒藥水,差別在於一個扣血一個損血
所以我們可以把他抽出來做成一個介面

1
2
3
4
public interface I_HpItem
{
int HpControl();
}

有了這個介面,就可以再往下分為紅藥水跟毒藥水

1
2
3
4
5
6
7
8
9
public class RedWater : I_HpItem
{
public int HpControl() { }
}

public class ToxinWater : I_HpItem
{
public int HpControl() { }
}

現在就可以將上面講的藥水公式帶進來了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class RedWater : I_HpItem
{
private int level;
public RedWater(int _level)
{
level = _level;
}

public int HpControl()
{
return 10 * level;
}
}

public class ToxinWater : I_HpItem
{
private int level;
public ToxinWater(int _level)
{
level = _level;
}

public int HpControl()
{
return -15 * level;
}
}

這樣以後如果要新增藥水的話,也只需要再使用I_HpItem介面做一個藥水,而不用改Character內的程式碼
而且這樣的好處也有可以將公式與角色分離,看起來也不會那麼凌亂

最後,使用起來像這樣

1
2
3
4
5
6
7
8
9
10
11
12
13
private void Start()
{

Character p1 = new Character();

p1.UseItem(new RedWater(p1.ChacaterLevel));
p1.ChacaterLevel = 2;
p1.UseItem(new RedWater(p1.ChacaterLevel));

p1.UseItem(new ToxinWater(p1.ChacaterLevel));
p1.UseItem(new ToxinWater(p1.ChacaterLevel));

}

當使用策略模式的時候,如果下次企劃跟你說要修改毒藥水為扣 玩家等級 x 30的血,就不用去修改原始的藥水數值
直接新增一個 [ ToxinWater_MinusDouble ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14

public class ToxinWater_MinusDouble : I_HpItem
{
private int level;
public ToxinWater(int _level)
{
level = _level;
}

public int HpControl()
{
return -30 * level;
}
}

然後在Main那邊就多了一個雙倍毒藥水可以用了,下次當企劃又覺得雙倍毒藥有點太難了,改回原始的好了,就可以直接使用原本的毒藥水了,就不用去修改毒藥水的扣血方式了!

1
2
3
4
5
6
7
8
9
private void Start()
{
Character p1 = new Character();

p1.ChacaterLevel = 2;
p1.UseItem(new ToxinWater_MinusDouble(p1.ChacaterLevel));
p1.UseItem(new ToxinWater(p1.ChacaterLevel));

}