定义
策略模式(Strategy Pattern):定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。
通俗理解
每天我们都会思考三个问题,早餐吃什么?午餐吃什么?晚餐吃什么?这三个问题也被称为人生的三大问题。在思考这三个问题的时候,我们需要考虑多种因素,价格、食物类型、口味、是否有折扣等等,在思考这些问题的过程中,我们就不禁陷了进去?我们应该吃什么呢?
对此,我想到了一个十分简单粗暴的方式,就是按照时间来选择。假设现在有三家店,店A、店B、店C。周一、三、五我们去店A,周二、四我们去店B,周末我们去店C。那么我们就可以很好地解决掉我们人生的三大问题了。
像这种有不同解决方案的,就是策略。我们现在预想着要怎么去实现它。第一个选择就是if(1 || 3 || 5){return A;}else if(2 || 4){return B;}else{return C;}
,很好,这是实现了。但是考虑到扩展性,如果我们2 去 D了,那么我得修改源代码吧?这样修改起来,就违反了开闭原则了。
怎么办?那就需要采用策略模式了,说白了,就是定义一个策略的接口类,然后具体的策略去实现这个接口。然后设定一个环境类,维护策略的成员变量,更加这个变量的实际值调用不同的策略就可以了。在选择食物这里,策略的接口指的是选择不同的店家,具体的策略就是店家的实现,环境就是人类,维护了店家的接口。
示例
渣渣程序
食物接口
public class Food {
public void selectFood(Day day) {
switch (day) {
case MON:System.out.println("A");break;
case TUES:System.out.println("B");break;
case WED:System.out.println("A");break;
case THUR:System.out.println("B");break;
case FRI:System.out.println("A");break;
case SAT:System.out.println("C");break;
case SUN:System.out.println("C");break;
default:System.out.println("A");
}
}
public enum Day{
MON,TUES, WED, THUR, FRI, SAT, SUN
}
}
程序入口
public class Main {
public static void main(String[] args) {
Food food = new Food();
food.selectFood(Food.Day.SAT);
}
}
//A
上面有两个问题:
- 职责不明确,选择店家不应该是食物选择的,而是人去选择;
- 扩展不友好,如果需要添加一个店家D,那么我需要修改源代码。
优化
类图
程序
食物接口和实现
public interface IFood {
void selectFood();
}
public class AFood implements IFood {
@Override
public void selectFood() {
System.out.println("A");
}
}
// BFood、CFood类似
人类
public class Human {
private IFood food;
// 构造器、setter、getter省略
public void selectFood() {
food.selectFood();
}
}
程序入口
public class Main {
public static void main(String[] args) {
IFood food = new AFood();
Human human = new Human(food);
human.selectFood();
}
}
//A
如果我需要添加店家D,那我只需要把店家D作为一个新的实现就可以了。
优点
- 符合开闭原则,不修改源代码的情况下添加新的功能;
- 管理算法族,使用继承把公用的代码放到抽象类当中;
- 避免多条件的if...else;
- 复用,策略提供给不同的环境使用。
缺点
- 需要知道调用哪个具体的策略类;
- 很多的策略实现;
- 无法使用多个策略,意思是不能在一个策略完成之后,调用另外一个策略。
应用场景
- 替换if...else...
程序
吐槽
其实还是感觉还是要用if...else来决定创建不同的对象,然后让他们入参,但是这个步骤移到了客户端(Main)来写,就是动态了,令人费解。其实还有一种方式,就是通过工厂模式去创建不同的对象,然后当做入参,if...else语句可以使用Xml配置文件去写,读取配置文件之后,然后根据配置文件的定义,去创建不同的对象。