引言
本篇主要通过一小篇漫画的形式给大家讲讲策略模式,由于策略模式本身不是很难,这里就不花太多的言辞描述了,一起看漫画吧
普通设计
从前有一个妈妈,她有一个叛逆的儿子,妈妈每天除了上下班就是要教育儿子,上下班的内容是固定的,现在每天唯一负责并且可能会变的就是教育儿子的方式,那么我们应该如何设计呢
对应的流程图如上,这种设计的代码实现如下
public class Main {
public static void main(String[] args) {
People mother = new People("妈妈", 31);
People son = new People("冤种儿子", 12);
System.out.println(mother.name+"上班....");
System.out.println(mother.name+"下班....");
System.out.println(mother.name+"准备教育");
System.out.println("弯腰拿拖鞋");
System.out.println("拿拖鞋抽"+son.name);
System.out.println("把拖鞋放回脚下");
System.out.println(mother.name+"教育结束");
}
}
输出如下
妈妈上班....
妈妈下班....
妈妈准备教育
弯腰拿拖鞋
拿拖鞋抽冤种儿子
把拖鞋放回脚下
妈妈教育结束
如果教育方式变成衣架要怎么改呢?
如上图可以看到就是将教育的流程改变下,对应的代码如下
public class Main {
public static void main(String[] args) {
People mother = new People("妈妈", 31);
People son = new People("冤种儿子", 12);
System.out.println(mother.name+"上班....");
System.out.println(mother.name+"下班....");
System.out.println(mother.name+"准备教育");
System.out.println("去走廊拿衣架");
System.out.println("拿衣架抽"+son.name);
System.out.println("回走廊放衣架");
System.out.println(mother.name+"教育结束");
}
}
输出如下
妈妈上班....
妈妈下班....
妈妈准备教育
去走廊拿衣架
拿衣架抽冤种儿子
回走廊放衣架
妈妈教育结束
这样改我们发现有两个问题
- 如果很多家庭都是这么教育(很多地方都是这么写),当涉及到教育方式变动时,改动就会很多
- 如果教育逻辑更加复杂的话,教育的逻辑容易跟“妈妈”的一天的内容耦合在一起,容易互相影响
策略模式设计
那应该怎么设计呢? “计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决”,此问题也不例外。通过抽象出一层策略层来解耦“妈妈”和教育方式,“妈妈”和教育方式的细节都依赖于策略层,“妈妈“只需要告诉策略层自己要选择哪一种教育方式即可,具体内部复杂的逻辑不需要”妈妈关心“,从而达到了”职责分离“的效果,很好的解决了上面描述的两个问题,具体实现如下
public interface Strategy {
void execute(People people);
}
class SlipperStrategy implements Strategy {
@Override
public void execute(People people) {
System.out.println("弯腰拿拖鞋");
System.out.println("拿拖鞋抽"+people.name);
System.out.println("把拖鞋放回脚下");
}
}
class HangerStrategy implements Strategy {
@Override
public void execute(People people) {
System.out.println("去走廊拿衣架");
System.out.println("拿衣架抽"+people.name);
System.out.println("回走廊放衣架");
}
}
class StickStrategy implements Strategy {
@Override
public void execute(People people) {
System.out.println("去卧室拿棒球棍");
System.out.println("拿棒球棍殴打"+people.name);
System.out.println("把棒球棍放回卧室");
}
}
接下来再看看使用方式
public class Main {
public static void main(String[] args) {
People mother = new People("妈妈", 31);
People son = new People("冤种儿子", 12);
System.out.println(mother.name+"上班....");
System.out.println(mother.name+"下班....");
mother.hit(new SlipperStrategy(), son);
}
}
class People {
String name;
int age;
public People(String name, int age) {
this.name = name;
this.age = age;
}
public void hit(Strategy strategy, People people) {
System.out.println(this.name+"准备教育");
strategy.execute(people);
System.out.println(this.name+"教育结束");
}
}
输出如下
妈妈上班....
妈妈下班....
妈妈准备教育
弯腰拿拖鞋
拿拖鞋抽冤种儿子
把拖鞋放回脚下
妈妈教育结束
如果想换一个教育方式只需改动传入hit方法的策略,即可例如
mother.hit(new StickStrategy(), son);
输出如下
妈妈上班....
妈妈下班....
妈妈准备教育
去卧室拿棒球棍
拿棒球棍殴打冤种儿子
把棒球棍放回卧室
妈妈教育结束
可以看到改动很小,即便后续有新的教育方式,只需增加新的策略类并改动这一行代码即可。这里的好处有
- 单一职责原则,教育方式是独立的逻辑,不该跟其他的逻辑耦合在一起,应该单独抽出来
- 开闭原则,面向修改关闭,面向拓展开放。在有新策略时可以新加一个策略类而不是修改之前的策略,避免影响到其他地方
基于上诉逻辑可能有人要问了,为什么不能将这些策略抽成方法呢,使用时再调用不也是一样吗?
答:接口相比方法来说跟规范以及有更多的限制,接口的边界是已经明确的设定好了,不允许做过多的改变从而限制程序不容易变得混乱