[DesignPattern]策略模式Strategy pattern簡易版分享+範例

  • 2972
  • 0

[DesignPattern]策略模式Strategy pattern簡易版分享+範例

用最簡單的一句話來解釋策略模式的話,就是抽象類別裡面的抽象method改用interface。

詳細解釋:
在超大型專案的時候,如果有一百個不同的類別去繼承抽象類別的時候,抽象類別裡面的method的程式碼就很容易產生重複撰寫,那之後要改就會很麻煩
舉例來說:假設抽象類別長這樣:(直接拿微軟的範例)這是一個洗衣機的抽象類別

abstract class WashingMachine
{
   public WashingMachine()
   {
      // Code to initialize the class goes here.
   }

   abstract public void Wash();
   abstract public void Rinse(int loadSize);//沖洗
   abstract public void Spin(int speed);//脫水
}

然後是繼承這個洗衣機抽象類別的實體類別:(也是直接拿微軟的copy過來)

class MyWashingMachine : WashingMachine
{
   public MyWashingMachine()
   {  
      // Initialization code goes here.    
   }

   override public void Wash()
   {
      // Wash code goes here.
   }

   override public void Rinse(int loadSize)
   {
      // Rinse code goes here.
   }

   override public void Spin(int speed)
   {
      // Spin code goes here.
   }
}

假設Panasonic要研發新的洗衣機,應該也是先用軟體寫模擬程式(假設就是用C#),不會直接車床開模子做實驗來研發新產品,應該沒人會這麼笨。

那用軟體在寫模擬程式的時候,新的產品類別是不是就要叫做MyWashingMachineNo1, MyWashingMachineNo2, ......MyWashingMachineNo100, 新研發出來的洗衣機越來越多, 這個抽象類別也就被繼承了上百次,這時候產生一個問題就是:
Spin()脫水的這個method,程式碼可能將會重複撰寫的超級多次!即使後來新的洗衣機越來越多高科技脫水方式來保護衣服,但是實際上真的脫水的技術可能就那麼2種(我猜的XD),對應到將來Panasonic變成百年企業之後的一百多種洗衣機(假設Panasonic一百年後還沒倒,我猜的 XD),那麼這一百種洗衣機的脫水的method將會很多重複、一模一樣的程式碼!萬一到時候要改脫水功能的程式碼,保證會改得很痛苦!
下面是兩個新產品,繼承上面的抽象類別,但是尚未採用策略模式,內容如下:MyWashingMachine1, MyWashingMachine2

class MyWashingMachine1 : WashingMachine
{
	public MyWashingMachine1()
	{
		// Initialization code goes here.    
	}

	override public void Wash()
	{
		// Wash code goes here.
	}

	override public void Rinse(int loadSize)
	{
		// Rinse code goes here.
	}

	override public void Spin(int speed)
	{
		// Spin code goes here.
		Console.WriteLine("傳統洗衣機進行脫水,轉速為" + speed.ToString());
	}
}

class MyWashingMachine2 : WashingMachine
{
	public MyWashingMachine2()
	{
		// Initialization code goes here.    
	}

	override public void Wash()
	{
		// Wash code goes here.
	}

	override public void Rinse(int loadSize)
	{
		// Rinse code goes here.
	}

	override public void Spin(int speed)
	{
		// Spin code goes here.
		Console.WriteLine("滾筒洗衣機進行脫水,轉速為" + speed.ToString());
	}
}

並且在Main裡面這樣呼叫就可以執行:

static void Main(string[] args)
{
	WashingMachine m1 = new MyWashingMachine1();
	WashingMachine m2 = new MyWashingMachine2();
	m1.Spin(200);
	m2.Spin(300);
	Console.WriteLine("press any key to continue");
	Console.ReadKey();

}

可以想像當新產品來到一百種、甚至兩百種的時候,下面這2行的程式碼,很有可能就要重複寫個一兩百次:畢竟脫水方法只有兩種,但是產品卻有上百種,重複的程式碼是必然的, 那之後如果要修改脫水的method程式碼,就得改一兩百次!

Console.WriteLine("傳統洗衣機進行脫水,轉速為" + speed.ToString());
Console.WriteLine("滾筒洗衣機進行脫水,轉速為" + speed.ToString());

這時候就是要用策略模式:先新增一個脫水用的interface, 並且實做出所有的脫水class:

interface ISpin
{
	void SpinMethod(int speed);
}

class MySpin1 : ISpin
{
	// Explicit interface member implementation: 
	void ISpin.SpinMethod(int speed)
	{
		// Method implementation.
		Console.WriteLine("傳統洗衣機進行脫水,轉速為" + speed.ToString());
	}

	
}

class MySpin2 : ISpin
{
	// Explicit interface member implementation: 
	void ISpin.SpinMethod(int speed)
	{
		// Method implementation.
		Console.WriteLine("滾筒洗衣機進行脫水,轉速為" + speed.ToString());
	}


}

然後回頭去改抽象類別WashingMachine類別,把抽象方法Spin()改成用interface:

abstract class WashingMachine
{
	public WashingMachine()
	{
		// Code to initialize the class goes here.
	}

	abstract public void Wash();
	abstract public void Rinse(int loadSize);//沖洗
	public void Spin(int speed)//脫水
	{
		sp.SpinMethod(speed);
	}
	protected ISpin sp;
}

至於兩個新產品MyWashingMachine1, MyWashingMachine2的脫水method就直接改用實做後的脫水class:

class MyWashingMachine1 : WashingMachine
{
	public MyWashingMachine1()
	{
		// Initialization code goes here.    
		this.sp = new MySpin1();//改用實做的脫水class
	}

	override public void Wash()
	{
		// Wash code goes here.
	}

	override public void Rinse(int loadSize)
	{
		// Rinse code goes here.
	}

	//override public void Spin(int speed)
	//{
	//    // Spin code goes here.
	//    Console.WriteLine("傳統洗衣機進行脫水,轉速為" + speed.ToString());
	//}
	
}

class MyWashingMachine2 : WashingMachine
{
	public MyWashingMachine2()
	{
		// Initialization code goes here.
		this.sp = new MySpin2();//改用實做的脫水class
	}

	override public void Wash()
	{
		// Wash code goes here.
	}

	override public void Rinse(int loadSize)
	{
		// Rinse code goes here.
	}

	//override public void Spin(int speed)
	//{
	//    // Spin code goes here.
	//    Console.WriteLine("滾筒洗衣機進行脫水,轉速為" + speed.ToString());
	//}
}

最後的執行結果一模一樣,且脫水的部分的程式碼就不會一直重複,且以後要是需要修改脫水部分的程式碼,只要改實做interface的Class:MySpin1, MySpin2就好了,不用改一、兩百次,是不是很方便呢?


 

參考資料:
interface (C# Reference)
Abstract Classes​
abstract (C# 參考)​
[Design Pattern] 策略模式 Strategy Pattern