Java設計模式--策略模式(以以角色游戲為背景為例)


今天不想寫代碼,給大家帶來一篇設計模式的文章,幫助大家可以把系統組織成容易了解、容易維護、具有彈性的架構。

先來看看策略模式的定義:

策略模式(Strategy Pattern):定義了算法族,分別封裝起來,讓它們之間可相互替換,此模式讓算法的變化獨立於使用算法的客戶。

好了,對於定義,肯定不是一眼就能看明白的,不然這篇文章就收尾了,對於定於大家簡單掃一眼,知道個大概,然后繼續讀下面的文章,讀完以后再來回味,效果嘎嘣脆。大家應該都玩過武俠角色游戲,下面我就以角色游戲為背景,為大家介紹:假設公司需要做一款武俠游戲,我們就是負責游戲的角色模塊,需求是這樣的:每個角色對應一個名字,每類角色對應一種樣子,每個角色擁有一個逃跑、攻擊、防御的技能。

初步的代碼:

 

 

  1. package com.zhy.bean;  
  2.   
  3. /** 
  4.  * 游戲的角色超類 
  5.  *  
  6.  * @author zhy 
  7.  *  
  8.  */  
  9. public abstract class Role  
  10. {  
  11.     protected String name;  
  12.   
  13.     protected abstract void display();  
  14.   
  15.     protected abstract void run();  
  16.   
  17.     protected abstract void attack();  
  18.   
  19.     protected abstract void defend();  
  20.   
  21. }  
package com.zhy.bean;

/**
 * 游戲的角色超類
 * 
 * @author zhy
 * 
 */
public abstract class Role
{
	protected String name;

	protected abstract void display();

	protected abstract void run();

	protected abstract void attack();

	protected abstract void defend();

}
  1. package com.zhy.bean;  
  2.   
  3. public class RoleA extends Role  
  4. {  
  5.     public RoleA(String name)  
  6.     {  
  7.         this.name = name;  
  8.     }  
  9.   
  10.     @Override  
  11.     protected void display()  
  12.     {  
  13.         System.out.println("樣子1");  
  14.     }  
  15.   
  16.     @Override  
  17.     protected void run()  
  18.     {  
  19.         System.out.println("金蟬脫殼");  
  20.     }  
  21.   
  22.     @Override  
  23.     protected void attack()  
  24.     {  
  25.         System.out.println("降龍十八掌");  
  26.     }  
  27.   
  28.     @Override  
  29.     protected void defend()  
  30.     {  
  31.         System.out.println("鐵頭功");  
  32.     }  
  33.   
  34. }  
package com.zhy.bean;

public class RoleA extends Role
{
	public RoleA(String name)
	{
		this.name = name;
	}

	@Override
	protected void display()
	{
		System.out.println("樣子1");
	}

	@Override
	protected void run()
	{
		System.out.println("金蟬脫殼");
	}

	@Override
	protected void attack()
	{
		System.out.println("降龍十八掌");
	}

	@Override
	protected void defend()
	{
		System.out.println("鐵頭功");
	}

}

沒幾分鍾,你寫好了上面的代碼,覺得已經充分發揮了OO的思想,正在竊喜,這時候項目經理說,再添加兩個角色

 

RoleB(樣子2 ,降龍十八掌,鐵布衫,金蟬脫殼)。

RoleC(樣子1,擁有九陽神功,鐵布衫,煙霧彈)。

於是你覺得沒問題,開始寫代碼,繼續集成Role,寫成下面的代碼:

 

  1. package com.zhy.bean;  
  2.   
  3. public class RoleB extends Role  
  4. {  
  5.     public RoleB(String name)  
  6.     {  
  7.         this.name = name;  
  8.     }  
  9.   
  10.     @Override  
  11.     protected void display()  
  12.     {  
  13.         System.out.println("樣子2");  
  14.     }  
  15.   
  16.     @Override  
  17.     protected void run()  
  18.     {  
  19.         System.out.println("金蟬脫殼");//從RoleA中拷貝  
  20.     }  
  21.   
  22.     @Override  
  23.     protected void attack()  
  24.     {  
  25.         System.out.println("降龍十八掌");//從RoleA中拷貝  
  26.     }  
  27.   
  28.     @Override  
  29.     protected void defend()  
  30.     {  
  31.         System.out.println("鐵布衫");  
  32.     }  
  33.   
  34. }  
package com.zhy.bean;

public class RoleB extends Role
{
	public RoleB(String name)
	{
		this.name = name;
	}

	@Override
	protected void display()
	{
		System.out.println("樣子2");
	}

	@Override
	protected void run()
	{
		System.out.println("金蟬脫殼");//從RoleA中拷貝
	}

	@Override
	protected void attack()
	{
		System.out.println("降龍十八掌");//從RoleA中拷貝
	}

	@Override
	protected void defend()
	{
		System.out.println("鐵布衫");
	}

}
  1. package com.zhy.bean;  
  2.   
  3. public class RoleC extends Role  
  4. {  
  5.     public RoleC(String name)  
  6.     {  
  7.         this.name = name;  
  8.     }  
  9.   
  10.     @Override  
  11.     protected void display()  
  12.     {  
  13.         System.out.println("樣子1");//從RoleA中拷貝  
  14.     }  
  15.   
  16.     @Override  
  17.     protected void run()  
  18.     {  
  19.         System.out.println("煙霧彈");  
  20.     }  
  21.   
  22.     @Override  
  23.     protected void attack()  
  24.     {  
  25.         System.out.println("九陽神功");  
  26.     }  
  27.   
  28.     @Override  
  29.     protected void defend()  
  30.     {  
  31.         System.out.println("鐵布衫");//從B中拷貝  
  32.     }  
  33.   
  34. }  
package com.zhy.bean;

public class RoleC extends Role
{
	public RoleC(String name)
	{
		this.name = name;
	}

	@Override
	protected void display()
	{
		System.out.println("樣子1");//從RoleA中拷貝
	}

	@Override
	protected void run()
	{
		System.out.println("煙霧彈");
	}

	@Override
	protected void attack()
	{
		System.out.println("九陽神功");
	}

	@Override
	protected void defend()
	{
		System.out.println("鐵布衫");//從B中拷貝
	}

}

寫完之后,你自己似乎沒有當初那么自信了,你發現代碼中已經存在相當多重復的代碼,需要考慮重新設計架構了。於是你想,要不把每個技能都寫成接口,有什么技能的角色實現什么接口,簡單一想,覺得這想法高大尚啊,但是實現起來會發現,接口並不能實現代碼的復用,每個實現接口的類,還是必須寫自己寫實現。於是,we need change ! 遵循設計的原則,找出應用中可能需要變化的部分,把它們獨立出來,不要和那些不需要變化的代碼混在一起。我們發現,對於每個角色的display,attack,defend,run都是有可能變化的,於是我們必須把這寫獨立出來。再根據另一個設計原則:針對接口(超類型)編程,而不是針對實現編程,於是我們把代碼改造成這樣:

 

  1. package com.zhy.bean;  
  2.   
  3. public interface IAttackBehavior  
  4. {  
  5.     void attack();  
  6. }  
package com.zhy.bean;

public interface IAttackBehavior
{
	void attack();
}
  1. package com.zhy.bean;  
  2.   
  3. public interface IDefendBehavior  
  4. {  
  5.     void defend();  
  6. }  
package com.zhy.bean;

public interface IDefendBehavior
{
	void defend();
}
  1. package com.zhy.bean;  
  2.   
  3. public interface IDisplayBehavior  
  4. {  
  5.     void display();  
  6. }  
package com.zhy.bean;

public interface IDisplayBehavior
{
	void display();
}
  1. package com.zhy.bean;  
  2.   
  3. public class AttackJY implements IAttackBehavior  
  4. {  
  5.   
  6.     @Override  
  7.     public void attack()  
  8.     {  
  9.         System.out.println("九陽神功!");  
  10.     }  
  11.   
  12. }  
package com.zhy.bean;

public class AttackJY implements IAttackBehavior
{

	@Override
	public void attack()
	{
		System.out.println("九陽神功!");
	}

}
  1. package com.zhy.bean;  
  2.   
  3. public class DefendTBS implements IDefendBehavior  
  4. {  
  5.   
  6.     @Override  
  7.     public void defend()  
  8.     {  
  9.         System.out.println("鐵布衫");  
  10.     }  
  11.   
  12. }  
package com.zhy.bean;

public class DefendTBS implements IDefendBehavior
{

	@Override
	public void defend()
	{
		System.out.println("鐵布衫");
	}

}
  1. package com.zhy.bean;  
  2.   
  3. public class RunJCTQ implements IRunBehavior  
  4. {  
  5.   
  6.     @Override  
  7.     public void run()  
  8.     {  
  9.         System.out.println("金蟬脫殼");  
  10.     }  
  11.   
  12. }  
package com.zhy.bean;

public class RunJCTQ implements IRunBehavior
{

	@Override
	public void run()
	{
		System.out.println("金蟬脫殼");
	}

}

這時候需要對Role的代碼做出改變:

 

 

  1. package com.zhy.bean;  
  2.   
  3. /** 
  4.  * 游戲的角色超類 
  5.  *  
  6.  * @author zhy 
  7.  *  
  8.  */  
  9. public abstract class Role  
  10. {  
  11.     protected String name;  
  12.   
  13.     protected IDefendBehavior defendBehavior;  
  14.     protected IDisplayBehavior displayBehavior;  
  15.     protected IRunBehavior runBehavior;  
  16.     protected IAttackBehavior attackBehavior;  
  17.   
  18.     public Role setDefendBehavior(IDefendBehavior defendBehavior)  
  19.     {  
  20.         this.defendBehavior = defendBehavior;  
  21.         return this;  
  22.     }  
  23.   
  24.     public Role setDisplayBehavior(IDisplayBehavior displayBehavior)  
  25.     {  
  26.         this.displayBehavior = displayBehavior;  
  27.         return this;  
  28.     }  
  29.   
  30.     public Role setRunBehavior(IRunBehavior runBehavior)  
  31.     {  
  32.         this.runBehavior = runBehavior;  
  33.         return this;  
  34.     }  
  35.   
  36.     public Role setAttackBehavior(IAttackBehavior attackBehavior)  
  37.     {  
  38.         this.attackBehavior = attackBehavior;  
  39.         return this;  
  40.     }  
  41.   
  42.     protected void display()  
  43.     {  
  44.         displayBehavior.display();  
  45.     }  
  46.   
  47.     protected void run()  
  48.     {  
  49.         runBehavior.run();  
  50.     }  
  51.   
  52.     protected void attack()  
  53.     {  
  54.         attackBehavior.attack();  
  55.     }  
  56.   
  57.     protected void defend()  
  58.     {  
  59.         defendBehavior.defend();  
  60.     }  
  61.   
  62. }  
package com.zhy.bean;

/**
 * 游戲的角色超類
 * 
 * @author zhy
 * 
 */
public abstract class Role
{
	protected String name;

	protected IDefendBehavior defendBehavior;
	protected IDisplayBehavior displayBehavior;
	protected IRunBehavior runBehavior;
	protected IAttackBehavior attackBehavior;

	public Role setDefendBehavior(IDefendBehavior defendBehavior)
	{
		this.defendBehavior = defendBehavior;
		return this;
	}

	public Role setDisplayBehavior(IDisplayBehavior displayBehavior)
	{
		this.displayBehavior = displayBehavior;
		return this;
	}

	public Role setRunBehavior(IRunBehavior runBehavior)
	{
		this.runBehavior = runBehavior;
		return this;
	}

	public Role setAttackBehavior(IAttackBehavior attackBehavior)
	{
		this.attackBehavior = attackBehavior;
		return this;
	}

	protected void display()
	{
		displayBehavior.display();
	}

	protected void run()
	{
		runBehavior.run();
	}

	protected void attack()
	{
		attackBehavior.attack();
	}

	protected void defend()
	{
		defendBehavior.defend();
	}

}

每個角色現在只需要一個name了:

 

 

  1. package com.zhy.bean;  
  2.   
  3. public class RoleA extends Role  
  4. {  
  5.     public RoleA(String name)  
  6.     {  
  7.         this.name = name;  
  8.     }  
  9.   
  10. }  
package com.zhy.bean;

public class RoleA extends Role
{
	public RoleA(String name)
	{
		this.name = name;
	}

}

現在我們需要一個金蟬脫殼,降龍十八掌!,鐵布衫,樣子1的角色A只需要這樣:

 

 

  1. package com.zhy.bean;  
  2.   
  3. public class Test  
  4. {  
  5.     public static void main(String[] args)  
  6.     {  
  7.   
  8.         Role roleA = new RoleA("A");  
  9.   
  10.         roleA.setAttackBehavior(new AttackXL())//  
  11.                 .setDefendBehavior(new DefendTBS())//  
  12.                 .setDisplayBehavior(new DisplayA())//  
  13.                 .setRunBehavior(new RunJCTQ());  
  14.         System.out.println(roleA.name + ":");  
  15.         roleA.run();  
  16.         roleA.attack();  
  17.         roleA.defend();  
  18.         roleA.display();  
  19.     }  
  20. }  
package com.zhy.bean;

public class Test
{
	public static void main(String[] args)
	{

		Role roleA = new RoleA("A");

		roleA.setAttackBehavior(new AttackXL())//
				.setDefendBehavior(new DefendTBS())//
				.setDisplayBehavior(new DisplayA())//
				.setRunBehavior(new RunJCTQ());
		System.out.println(roleA.name + ":");
		roleA.run();
		roleA.attack();
		roleA.defend();
		roleA.display();
	}
}

經過我們的修改,現在所有的技能的實現做到了100%的復用,並且隨便項目經理需要什么樣的角色,對於我們來說只需要動態設置一下技能和展示方式,是不是很完美。恭喜你,現在你已經學會了策略模式,現在我們回到定義,定義上的算法族:其實就是上述例子的技能;定義上的客戶:其實就是RoleA,RoleB...;我們已經定義了一個算法族(各種技能),且根據需求可以進行相互替換,算法(各種技能)的實現獨立於客戶(角色)。現在是不是很好理解策略模式的定義了。

附上一張UML圖,方便大家理解:

最后總結一下OO的原則:

1、封裝變化(把可能變化的代碼封裝起來)

2、多用組合,少用繼承(我們使用組合的方式,為客戶設置了算法)

3、針對接口編程,不針對實現(對於Role類的設計完全的針對角色,和技能的實現沒有關系)

 

 

點擊此處下載源碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM