策略模式(Strategy Pattern)是一种比较简单的模式,也叫做政策模式(Policy Pattern),其定义如下:Define a family of algorithms,encapsulate each one,and make them interchangeable.(定义一组
算法,将每个算法都封装起来,并且使它们之间可以互换。)-设计模式之禅
案例:
一家商场在年底推出活动,全场商品一律打八折,部分商品在全场打八折的基础上在七折,然后金额满300减20.使用策略模式怎么来设计这个场景
不管是八折 七折 还是满减 都是不同的算法机制。首先定义一个计算的接口出来。
package com.zcm.strategy2;
import java.math.*;
/**
* @Description:计算
* 一家商场在年底推出活动,
* 全场商品一律打八折,
* 部分商品作为特价处理,
* 部分商品在全场打八折的基础上在七折,
* 然后实付金额满300减20.使用策略模式怎么来设计这个场景
* @Author: zcm
* @Version:v.2.3.0
* @Date:2021/1/23 15:13
*/
public interface PriceIStrategy<T> {
/**
* @Description:计算价格并返回
* @Author: zcm
* @Version:v.2.3.0
* @Date:2021/1/23 15:12
*/
BigDecimal compute(T t);
}
全场八折的算法【第一种策略算法】
package com.zcm.strategy2;
import java.math.*;
/**
* @program: DemoZCM
* @ClassName Audience
* @Description
* @Author zcm
* @Date 2021/1/23 15:21
* @Version V1.0
*/
public class AudienceEight implements PriceIStrategy<Goods> {
@Override
public BigDecimal compute(Goods goods) {
BigDecimal price = goods.getPrice();
return price.multiply(new BigDecimal(0.8)).setScale(2, BigDecimal.ROUND_HALF_UP);
}
}
七折的算法【第二种策略算法】
package com.zcm.strategy2;
import java.math.*;
/**
* @program: DemoZCM
* @ClassName AudienSeven
* @Description
* @Author zcm
* @Date 2021/1/23 15:24
* @Version V1.0
*/
public class AudienceSeven implements PriceIStrategy<Goods>{
@Override
public BigDecimal compute(Goods goods) {
BigDecimal price = goods.getPrice();
return price.multiply(new BigDecimal(0.7)).setScale(2, BigDecimal.ROUND_HALF_UP);
}
}
满减的算法【第三种策略算法】
package com.zcm.strategy2;
import java.math.*;
/**
* @program: DemoZCM
* @ClassName FullReduction
* @Description
* @Author zcm
* @Date 2021/1/23 15:30
* @Version V1.0
*/
public class FullReduction implements PriceIStrategy<Goods> {
@Override
public BigDecimal compute(Goods goods) {
BigDecimal price = goods.getPrice();
if (price.compareTo(new BigDecimal(300)) > 0) {
BigDecimal subtract = price.subtract(new BigDecimal(20));
return subtract;
} else {
return goods.getPrice();
}
}
}
实体类
/***********************************************************************
* Module: Album.java
* Author: Administrator
* Purpose: Defines the Class Album
***********************************************************************/
package com.zcm.strategy2;
import lombok.*;
import java.math.*;
import java.util.*;
/**
* 相册
*
* @pdOid 6cdc5177-846d-4273-b22f-47a6ec081c42
*/
@Data
public class Goods {
private String id;
/**
* @Description:价格
* @Author: zcm
* @Version:v.2.3.0
* @Date:2021/1/23 15:17
*/
protected BigDecimal price;
/**
*@Description:商品名
*@Author: zcm
*@Version:v.2.3.0
*@Date:2021/1/23 15:17
*/
protected String name;
public Goods(String id, BigDecimal price, String name) {
this.id = id;
this.price = price;
this.name = name;
}
public Goods() {
}
}
封装策略的上下文,也可已在这个里面去组装复杂的算法
package com.zcm.strategy2;
import java.math.*;
/**
* @program: DemoZCM
* @ClassName GoodsPriceComparator
* @Description
* @Author zcm
* @Date 2021/1/23 15:40
* @Version V1.0
*/
public class StrategyContext {
private PriceIStrategy priceIStrategy;
public StrategyContext(PriceIStrategy priceIStrategy) {
this.priceIStrategy = priceIStrategy;
}
public StrategyContext() {
}
/**
* @Description:计算商品价格
* @Author: zcm
* @Version:v.2.3.0
* @Date:2021/1/23 16:06
*/
public Goods comparator(Goods goods) {
BigDecimal price = this.priceIStrategy.compute(goods);
goods.setPrice(price);
return goods;
}
}
使用 Context 来查看当它改变策略 Strategy 时的行为变化。
package com.zcm.strategy2;
import java.math.*;
/**
* @program: DemoZCM
* @ClassName Test
* @Description
* @Author zcm
* @Date 2021/1/23 15:25
* @Version V1.0
*/
public class Test {
public static void main(String[] args) {
StrategyContext context = new StrategyContext(new AudienceEight());
BigDecimal price = new BigDecimal(599);
System.out.println("商品原价:" + price);
/**
* 全场打八折
*/
System.out.println("--------------------------全场打八折-----------------------------");
Goods goods = context.comparator(new Goods("123", price, "连帽羽绒服"));
System.out.println("打完八折后的商品价格:" + goods.getName() + ",价格:" + goods.getPrice());
/**
* 在打八折的基础上再打七折
*/
System.out.println("-----------------------在打八折的基础上再打七折----------------------");
context = new StrategyContext(new AudienceSeven());
goods = context.comparator(goods);
System.out.println("八折的基础上在打七折后的商品价格:" + goods.getName() + ",价格:" + goods.getPrice());
/**
* 最后满300减20
*/
System.out.println("--------------------------最后满300减20--------------------------");
context = new StrategyContext(new FullReduction());
goods = context.comparator(goods);
System.out.println("最后满300减20后的商品价格:" + goods.getName() + ",价格:" + goods.getPrice());
}
}
为什么使用策略模式?
在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
使用策略模式有什么优缺点?
优点:
1、算法可以*切换。
这是策略模式本身定义的,只要实现抽象策略,它就成为策略家族的一个成员,通过封 装角色对其进行封装,保证对外提供“可*切换”的策略
2、避免使用多重条件判断。
没有策略模式的情况下,加入需要加减乘除,使用多重的条件语句?多重条件语句不易维护,而且出错的概率大大增强。使用策略模式后,可以由其他模块决定采用何种策略,策略
家族对外提供的访问接口就是封装类,简化了操作,同时避免了条件语句判断。
3、扩展性良好。
想要增加一种新的算法,只需要实现策略接口就好了。
缺点:
1.策略类会增多。
每一个策略都是一个类,复用的可能性很小,类数量增多。
2.所有的策略类都需要对外暴露。
上层模块必须知道有哪些策略,然后才能决定使用哪一个策略,这与迪米特法则是相违背的,我只是想使用了一个策略,我凭什么就要了解这个策略呢?那要你的封装类还有什么意义?这是原装策略模式的一个缺点,幸运的是,我们可以使用其他模式来修正这个缺陷,如工厂方法模式、代理模式或享元模式。
使用场景:
1.多个类只要在算法或者行为上面相似的场景。
2.算法*切换的场景。
3. 需要屏蔽算法规则的场景。
注意事项:如果一个系统的策略多于4个,就需要考虑使用混合模式,解决策略类膨胀的问题。
总结:
策略模式是一个非常简单的模式。它在项目中使用得非常多,但它单独使用的地方就比较少了,因为它有致命缺陷:所有的策略都需要暴露出去,这样才方便客户端决定使用哪一个策略