java设计模式 - 策略模式

版权声明:本文为博主原创文章,未经博主允许请随便转载 https://blog.csdn.net/CSDN_Terence/article/details/82389301

设计一个小游戏

需求

* 要设计这样一个应用小游戏,游戏里面有不同类的任务,他们通过战斗打架赢得胜利,这些人物通过使用武器和使用法术战斗。
* 所有的人物都可以以超声速的速度奔跑;
* 其中国王擅长使用长剑,并且还会一种赤火焰神术,并且拥有学习能力,每次战斗之后都可以学习到对方的法术;
* 还有精灵,擅长使用弓箭,还会使用法术深林之光。
* 初次之外,还有很多其他任务,骑士,魔法师,贵族,平民等。
* 这一天,国王和精灵碰见了,一言不合,大打出手。

讨论分析

我想用面向对象的思想,用角色超类定义属性,他们都有奔跑,使用武器,使用法术的技能,具体使用什么样的武器什么样的法术,交给实现类,如果拥有相同的技能,则可以使用类的继承来解决。
* 不累么?
* 这样如果两个人物拥有相同的方法,又有不同的方法,岂不是要继承又要通过复写来实现自己的技能么,一个两个人物可以这样,如果十几中不同类型的游戏人物怎么办?维护的时候是不是每次都要修改代码?
如果添加的技能更多?人物更多,维护起来岂不是要爆头?
* 是否可以尝试将变化的部分(即使用武器和法术的行为)抽取出来单一封装呢?这样维护的时候只用关注变化的部分。
* 是否可以尝试将角色类中引入这部分变化的行为呢?但是,此时可能出现另外一种情况,不同的技能行为,难道要每个都引入么,当然不是了,此时,可以考虑一下多态,用接口定义技能行为,具体的实现对象不要管,交给具体的角色去实现,而不是现在的抽象角色。

小游戏设计代码

/** * @desc: 使用法术战斗 */
public interface SpellsBehavior {
  void useSpells();
}

/** * @desc: 使用武器战斗 */
public interface WeaponBehavior {
  void useWeapon();
}
/**三个使用不同武器的实现类*/
public class BowAndArrowBehavior implements WeaponBehavior {
  public void useWeapon(){
	System.out.println("…使用弓箭…");
  }
}
public class SwordBehavior implements WeaponBehavior {
  public void useWeapon(){
    System.out.println("…使用宝剑…");
  }
}
public class HandBehavior implements WeaponBehavior {
  public void useWeapon(){
	System.out.println("…赤手搏击…");
  }
}
/** * @desc: 两个使用法术的实现类 */
public class BlazeBehavior implements SpellsBehavior {
  public void useSpells(){
	System.out.println("…使用赤火炎…");
  }
}
public class TreeLightBehavior implements SpellsBehavior {
  public void useSpells(){
    System.out.println("…使用森林之光…");
  }
}

抽象的角色类

/** * @desc: 抽象的角色 */
public abstract class Character {
  private WeaponBehavior weaponBehavior;
  private SpellsBehavior spellsBehavior;

  /**   * 抽象方法,待具体的角色去实现   */
  public abstract void display();

  /**   * 将具体的战斗方法通过接口引用变量委托给具体的角色去实现   */
  public void fightWithWeapon(){
    weaponBehavior.useWeapon();
  }
  public void fightWithSpells(){
    spellsBehavior.useSpells();
  }
  public void runOverSound(){
	System.out.println("…超声速奔跑…");
  }

  public void setWeaponBehavior(WeaponBehavior weaponBehavior) {
	this.weaponBehavior = weaponBehavior;
  }
  public void setSpellsBehavior(SpellsBehavior spellsBehavior) {
	this.spellsBehavior = spellsBehavior;
  }
}

具体的角色类

具体的角色要有具体的技能,要有自己的武器和法术

/** * @desc: 国王 */
public class King extends Character {
  public King(){
    setWeaponBehavior(new SwordBehavior());
    setSpellsBehavior(new BlazeBehavior());
  }
  public void  display(){
	System.out.println("King : ");
  }
}
/** * @desc: 精灵 */
public class Spirit extends Character {
  public Spirit(){
	setWeaponBehavior(new BowAndArrowBehavior());
	setSpellsBehavior(new TreeLightBehavior());
  }
  public void  display(){
	System.out.println("Spirit : ");
  }
}

战斗测试类

public class Test {
  public static  void main(String[] args){
    //精灵的战斗
    Character spirit=new Spirit();
    spirit.display();
    spirit.fightWithWeapon();
    spirit.fightWithSpells();
    spirit.runOverSound();
	//国王的战斗,并学习了新的法术技能
	Character king=new King();
	king.display();
	king.fightWithWeapon();
	king.fightWithSpells();
	spirit.runOverSound();
	System.out.print("学习后:");
	king.setSpellsBehavior(new TreeLightBehavior());
	king.fightWithSpells();
  }
}

策略模式

设计原则

(1)动静分离,封装变化。
   将要设计的对象中可能发生变化(动)的部分抽离出来,单独封装独立起来。将公有的属性封装进抽象类中,该抽象类的所有实现类都可以使用这种共有不变(静)的属性。
   抽离封装变化,以后可以轻易的改动和扩充此部分,而不影响不需要变化的部分。增加了系统的弹性。
   比如,所有人员都有超声速奔跑这一技能就属于静的部分,放在抽象类Character中,使用武器和使用法术的技能可能会出现不同的表现形式,则分离出来商定好接口协议,然后按照不同类型的技能分别实现。
(2)针对接口编程,而非针对实现编程。
   对变化的部分设计接口协议,利用不同的实现类实现同一接口。而同一接口的所有实现类叫做这个接口的算法族。
   例如,使用宝剑的行为实现类和使用弓箭的行为实现类共同实现了使用武器的接口,使用宝剑和使用武器则都是使用武器的算法族的算法。
(3)多用组合,少用继承。
   组合就是一个类中使用了另一个类的现象。使用组合能够增加系统的弹性,不仅可以将算法组封装成类,更可以“在运时动态的改变执行行为”。 比如说Character这一抽象类:
   含有公共行为超声速奔跑(有具体实现)。
   利用组合(has a)声明了接口类型的行为引用变量weaponBehavior和spellsBehavior。
   同时定义了执行该行为的方法,但是怎么去执行该行为则没有具体的过程(没有实现行为,仅仅是调用了行为方法),而是将具体的行为委托给了具体的行为类去实现。
   行为类的实例化则是交给了character抽象类的具体子类去实现,意思就是,该子类所代表的角色需要什么技能就去实例化setBehiavior一个怎么样的行为技能,也就是set一个算法族中想要的算法技能。
   结合具体的例子,该具体角色也可以在实例化时赋予想要的行为技能,然后通过set方法去更换行为技能,那么,同一角色的一个行为引用,执行时会出不一样的行为。很明显,在这一点上,体现了面向对象的多态性。

* * 避免了因为变化的部分带来的代码覆盖和重复实现等缺点,利用接口将变化独立出去,根据需要变化行为,充分体现了多态。
* * 体现出来OO中的抽象,封装,继承,多态等重要特性。

概念定义

策略模式:定义了算法族,分别封装起来,让他们之间可以相互替换,该模式让算法变化独立于使用算法的客户(角色)。

策略模式的特点

策略模式的优点
 (1)策略模式的功能就是通过抽象、封装来定义一系列的算法,使得这些算法可以相互替换,所以为这些算法定义一个公共的接口,以约束这些算法的功能实现。如果这些算法具有公共的功能,可以将接口变为抽象类,将公共功能放到抽象父类里面。
 (2)策略模式的一系列算法是可以相互替换的、是平等的,写在一起就是if-else组织结构,如果算法实现里又有条件语句,就构成了多重条件语句,可以用策略模式,避免这样的多重条件语句。
 (3)扩展性更好:在策略模式中扩展策略实现非常的容易,只要新增一个策略实现类,然后在使用策略实现的地方,使用这个新的策略实现就好了。
策略模式的缺点
(1)客户端必须了解所有的策略,清楚它们的不同:
 如果由客户端来决定使用何种算法,那客户端必须知道所有的策略,清楚各个策略的功能和不同,这样才能做出正确的选择,但是这暴露了策略的具体实现。
 (2)增加了对象的数量:
 由于策略模式将每个具体的算法都单独封装为一个策略类,如果可选的策略有很多的话,那对象的数量也会很多。
 (3)只适合偏平的算法结构:
 由于策略模式的各个策略实现是平等的关系(可相互替换),实际上就构成了一个扁平的算法结构。即一个策略接口下面有多个平等的策略实现(多个策略实现是兄弟关系),并且运行时只能有一个算法被使用。这就限制了算法的使用层级,且不能被嵌套。
策略模式的本质
 分离算法,选择实现。
 如果你仔细思考策略模式的结构和功能的话,就会发现:如果没有上下文,策略模式就回到了最基本的接口和实现了,只要是面向接口编程,就能够享受到面向接口编程带来的好处,通过一个统一的策略接口来封装和分离各个具体的策略实现,无需关系具体的策略实现。
  貌似没有上下文什么事,但是如果没有上下文的话,客户端就必须直接和具体的策略实现进行交互了,尤其是需要提供一些公共功能或者是存储一些状态的时候,会大大增加客户端使用的难度;引入上下文之后,这部分工作可以由上下文来完成,客户端只需要和上下文进行交互就可以了。这样可以让策略模式更具有整体性,客户端也更加的简单。
  策略模式体现了开闭原则:策略模式把一系列的可变算法进行封装,从而定义了良好的程序结构,在出现新的算法的时候,可以很容易的将新的算法实现加入到已有的系统中,而已有的实现不需要修改。
  策略模式体现了里氏替换原则:策略模式是一个扁平的结构,各个策略实现都是兄弟关系,实现了同一个接口或者继承了同一个抽象类。这样只要使用策略的客户端保持面向抽象编程,就可以动态的切换不同的策略实现以进行替换。

猜你喜欢

转载自blog.csdn.net/CSDN_Terence/article/details/82389301