策略模式
策略模式一般用项目中算法功能的切换。对于解决某一个问题,假设存在一个算法族,如果希望某些时刻从其中选择一个合适的算法来实现功能,那么可以选择使用策略模式。策略模式将算法的表示功能和使用功能相分离,可以很方便的更换算法或者增加新的算法,符合开闭原则在实际应用中也比较常见。
策略模式的UML类图和状态模式比较相似,但两者在使用上存在根本的区别。状态模式是根据传入的参数,被动的选择合适的算法执行,因此必须有方法来控制状态的选择,比如购物的时候根据不同的金额自动计算不同的优惠价格;而策略模式是由调用者主动选择某种策略,比如去某地方式有步行、骑车、打车等,任选其一。
定义
定义一系列算法, 将每一个算法封装起来,并让它们可以互相替换。策略模式让算法可以独立于使用它的客户而变化。
结构
- Context(环境类):调用算法的角色,内部维护了一个策略类实例,调用策略类的方法
- Strategy(抽象策略类):抽象策略类为所有的算法声明了抽象方法,是所有实例算法的父类,被环境类对象调用
- ConcreteStrategy(具体策略类):抽象策略类的子类,封装了具体的策略方法,实际被调用的对象
实例
// 为不同的日子设置了不同的打折策略。平常不打折,国庆8折。比较复杂的还可以比如六一儿童节,针对类型为儿童产品的商品打5折,其他商品不打折。
import java.math.BigDecimal;
import java.util.List;
interface CaculateMethod {
BigDecimal caculate(List<Shop> shops);
}
import java.math.BigDecimal;
import java.util.List;
public class NationalDayCaculateMethod implements CaculateMethod{
// 国庆节计算方式打8折
@Override
public BigDecimal caculate(List<Shop> shops) {
return shops.stream().map(Shop::getPrice).reduce(BigDecimal.ZERO,(a, b)->a.add(b)).multiply(new BigDecimal("0.8"));
}
}
import java.math.BigDecimal;
import java.util.List;
public class DayCaculateMethod implements CaculateMethod{
// 平常不打折
@Override
public BigDecimal caculate(List<Shop> shops) {
return shops.stream().map(Shop::getPrice).reduce(BigDecimal.ZERO,(a, b)->a.add(b));
}
}
import java.math.BigDecimal;
public class Shop {
private Integer type;
private String name;
private BigDecimal price;
public Shop(Integer type, String name, BigDecimal price) {
this.type = type;
this.name = name;
this.price = price;
}
public Integer getType() {
return type;
}
public String getName() {
return name;
}
public BigDecimal getPrice() {
return price;
}
}
import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
public class Person {
private List<Shop> shops;
private CaculateMethod caculateMethod;
public Person(CaculateMethod caculateMethod) {
this.caculateMethod = caculateMethod;
}
public void setShops(List<Shop> shops) {
this.shops = shops;
}
public BigDecimal caculate() {
if (Objects.nonNull(shops) && shops.size() > 0) {
return caculateMethod.caculate(shops);
}
return BigDecimal.ZERO;
}
}
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
CaculateMethod method = new NationalDayCaculateMethod();
Person p = new Person(method);
Shop shop1 = new Shop(1, "面包", new BigDecimal("10"));
Shop shop2 = new Shop(2, "玩具车", new BigDecimal("1000"));
Shop shop3 = new Shop(3, "网球", new BigDecimal("30"));
List<Shop> shops = new ArrayList<>();
shops.add(shop1);
shops.add(shop2);
shops.add(shop3);
p.setShops(shops);
System.out.println(p.caculate());
}
}
优点
- 策略模式符合开闭原则,对于新增策略,不需修改原有代码
- 将策略内容单独分离出来,方便代码的复用
- 避免了多重条件的判断
缺点
- 调用者必须知道每个策略执行的方式,必须要了解每个策略
- 一点细小的不同可能就需要新的策略,导致类的增多
- 无法同时使用多个策略类
适用场景
- 系统需要在多个算法中动态切换
- 对象有多重行为,每个行为有不同的判断来引用