:第一章 浅谈设计模式 之 策略模式(Strategy)
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了减少冗余代码,提高代码的重用性,并且使对象之间的交互更加的松耦合,便于程序的无侵入扩展,从而保证代码的可靠性,比如大名鼎鼎的Spring框架便是集合众多优秀设计模式的典型代表(适配,单例、工厂、代理、策略、模板、装饰等等。。。)。
本文主要介绍策略模式的设计思想及示例。
定义一系列的算法,将它们分别封装起来,让它们之间在运行期可以互相替换,此模式让算法的变化独立于使用算法的客户 。
简单来说就是把一组对象可变的行为抽象出来,单独封装成一个一个的方法,然后在运行期根据不同的行为策略去实例化对象。
如果不用策略模式来设计,我们先来想一个场景:有一个抽象角色Role.class类,里面定义一个抽象的fight()打架方法,再定义两个 战士 和 法师 类去继承它:Soldier.calss,Sorcerer.class,战士 和 法师都可以来装备不同的武器,所以在Soldier和Sorcerer类中需要定义多个装备不同武器的方法,具体代码示例如下:
/**
* 角色抽象类
*/
public abstract class Role {
String weapon;//武器
public void setWeapon(String weapon){
this.weapon = weapon;
}
//打架方法
abstract void fight();
}
/**
* 战士
*/
public class Soldier extends Role {
void fight() {
if("Sword".equals(weapon)){
equipmentSword();
}else if("Spear".equals(weapon)){
equipmentSpear();
}
System.out.println("fight!");
}
void equipmentSword(){
System.out.println("Soldier使用了新武器:Sword 饮血剑");
}
void equipmentSpear(){
System.out.println("Soldier使用了新武器:Spear 长矛");
}
}
/**
* 法师
*/
public class Sorcerer extends Role {
void fight() {
if("Truncheon".equals(weapon)){
equipmentTruncheon();
}else if("Sword".equals(weapon)){
equipmentSword();
}
System.out.println("fight!");
}
void equipmentTruncheon(){
System.out.println("Sorcerer使用了新武器:Truncheon 法杖");
}
void equipmentSword(){
System.out.println("Sorcerer使用了新武器:Sword 饮血剑");
}
}
main方法测试
public class TestMain {
public static void main(String[] args) {
Role sorcerer = new Sorcerer();
sorcerer.setWeapon("Sword");
sorcerer.fight();
sorcerer.setWeapon("Truncheon");
sorcerer.fight();
}
}
测试输出结果:
Sorcerer使用了新武器:Sword 饮血剑
fight!
Sorcerer使用了新武器:Truncheon 法杖
fight!
Process finished with exit code 0
上面的写法比较简单,看似也没什么问题,但如果仔细分析的话,这样的写法问题还是挺大的,首先第一个问题 Sorcerer 和Soldier 类中的fight()方法包含所有的装备武器方法,那么如果后期要继续增加新的武器,fight()方法就会越来越庞杂,这样明显违背了设计中的开闭原则(对修改关闭,对扩展开放),第二个问题 武器和角色之间会形成强耦合,试想一下,以后每新增一个武器,就要在原有代码中去新增一个方法,这也不利于代码的扩展。
那么,明白了问题所在,应该怎样去设计才能既松耦合,又利于代码动态扩展呢?下面我们将代码改造一下:
我们从上面的案例中可以发现,Sorcerer 和 Soldier 他们共有的行为是fight()打架,不同的行为是装备不同的武器方法,那么我们可以将不同的行为抽象出来单独封装起来,新建武器类接口,里面只定义一个获取武器的方法getWeapon():
//武器策略接口
public interface WeaponStrategy {
String getWeapon();
}
新建三个具体的武器类 Truncheon(法杖),Sword(饮血剑),Spear(长矛)去实现它
/**
* 长矛
*/
public class Spear implements WeaponStrategy{
public String getWeapon() {
return "Spear 长矛";
}
}
/**
* 饮血剑
*/
public class Sword implements WeaponStrategy{
public String getWeapon() {
return "Sword 饮血剑";
}
}
/**
* 法杖
*/
public class Truncheon implements WeaponStrategy{
public String getWeapon() {
return "Truncheon 法杖";
}
}
接下来我们需要把原有的Role类改造一下,里面维护一个武器接口变量WeaponStrategy,通过set方法给它赋值
/**
* 角色抽象类
*/
public abstract class Role {
WeaponStrategy weaponStrategy;//武器策略接口
protected abstract void fight();//打架
protected abstract void equipmentWeapon(WeaponStrategy weaponStrategy);//装备武器
public void setWeaponStrategy(WeaponStrategy weaponStrategy){
this.weaponStrategy = weaponStrategy;
}
}
最后,Sorcerer 和 Soldier类去继承Role类并去实现这两个方法
/**
* 战士
*/
public class Soldier extends Role {
protected void fight() {
equipmentWeapon(weaponStrategy);
System.out.println("Soldier使用了新武器:<"+weaponStrategy.getWeapon()+"> fight!");
}
protected void equipmentWeapon(WeaponStrategy weaponStrategy) {
System.out.println("Soldier装备了武器:"+weaponStrategy.getWeapon());
}
}
/**
* 法师
*/
public class Sorcerer extends Role {
protected void fight() {
equipmentWeapon(weaponStrategy);
System.out.println("Sorcerer使用了新武器:<"+weaponStrategy.getWeapon()+"> fight!");
}
protected void equipmentWeapon(WeaponStrategy weaponStrategy) {
System.out.println("Sorcerer装备了武器:"+weaponStrategy.getWeapon());
}
}
至此,代码改造完成,接下来进行测试
public class TestMain {
public static void main(String[] args) {
Role soldier = new Soldier();
soldier.setWeaponStrategy(new Sword());
soldier.fight();
soldier.setWeaponStrategy(new Spear());
soldier.fight();
soldier.setWeaponStrategy(new Truncheon());
soldier.fight();
}
}
测试输出结果:
Soldier装备了武器:Sword 饮血剑
Soldier使用了新武器:<Sword 饮血剑> fight!
Soldier装备了武器:Spear 长矛
Soldier使用了新武器:<Spear 长矛> fight!
Soldier装备了武器:Truncheon 法杖
Soldier使用了新武器:<Truncheon 法杖> fight!
Process finished with exit code 0
从上例中设计思想可以发现,Role类中维护的是武器的接口超类而不是具体的实现,Role类和武器类之间也无继承关系,而是通过对象之间的组合,实现了松耦合,这样之后如果新增一个武器,只需要去新增一个具体的武器类去实现武器接口而不用去修改原有代码逻辑,这就是策略模式带来的显著的好处,遵循的设计原则如下:
1. 封装改变,把可变的行为抽象出来
2. 针对接口编程,而不是针对实现编程
3. 多用组合,少用继承
4. 开闭原则,对修改关闭,对扩展开放
在实际项目设计发中,是否要去选用策略模式,要针对具体的业务具体分析,选择一个好的设计模式能让你的代码更优雅,更可靠!
最后,如果本篇文章对您有所帮助,可以评论或点赞支持一下哦,感谢感谢!