策略模式是什么?
策略模式就是定义一个算法族,封装起来,算法族可以相互替换,根据需求,来选择相应的算法!
说的太官方了哦,都搞懵逼了,其实可以举一个男海王的例子,他有3个女朋友,第一个女朋友喜欢猛男,他就可以把自己打扮成猛男的样子;第二个女朋友喜欢奶一点,那他就把自己打扮成奶一点的样子;第三个女朋友该奶的时候奶,该狼的时候狼,他就可以兼顾!什么意思那,就是对于三个女朋友来说,男海王有三种不同的策略!这三种策略相当于一个算法族,并且可以相互替换!
为什么需要了解策略模式?
(看过我之前设计模式的朋友,肯定只要我要说啥了,来个传送门)使用策略模式,可以遵循设计原则(设计原则看这篇就够了),比如遵循开闭原则,每个策略相当于一个需求,需求是变的,使用策略模式,我们只需要添加新的需求就行了,不需要关注也业务的联系!在比如单一职责原则,每个策略相当于一个类,一个类只表示一种策略!
通过代码进一步理解策略模式
我们举一个客户下订单的例子,订单的价格会根据订单量或者客户类别来打折!
客户实体类
/**
* @author: tianjx
* @date: 2022/1/12 10:45
* @description: 客户实体类
*/
public class Customer {
/**
* 客户编号
*/
private String customerNo;
/**
* 客户名称
*/
private String customerName;
/**
* 客户级别 A,B,C
*
*/
private String customerCalss;
public Customer() {}
public Customer(String customerNo, String customerName, String customerCalss) {
this.customerNo = customerNo;
this.customerName = customerName;
this.customerCalss = customerCalss;
}
public String getCustomerNo() {
return customerNo;
}
public void setCustomerNo(String customerNo) {
this.customerNo = customerNo;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getCustomerCalss() {
return customerCalss;
}
public void setCustomerCalss(String customerCalss) {
this.customerCalss = customerCalss;
}
@Override
public String toString() {
return "Customer{" +
"customerNo='" + customerNo + '\'' +
", customerName='" + customerName + '\'' +
", customerCalss='" + customerCalss + '\'' +
'}';
}
}
订单实体类
/**
* @author: tianjx
* @date: 2022/1/12 10:56
* @description: 订单类
*/
public class Order {
// 客户
private Customer customer;
// 产品名称
private String product;
// 数量
private BigDecimal num;
// 单价
private BigDecimal price;
// 折扣
private BigDecimal discount;
// 总价
private BigDecimal amount;
// 折扣总金额
private BigDecimal actualAmount;
public Order() {
}
public Order(Customer customer, String product, BigDecimal num, BigDecimal price) {
this.customer = customer;
this.product = product;
this.num = num;
this.price = price;
this.amount = amount;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public String getProduct() {
return product;
}
public void setProduct(String product) {
this.product = product;
}
public BigDecimal getNum() {
return num;
}
public void setNum(BigDecimal num) {
this.num = num;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public BigDecimal getDiscount() {
return discount;
}
public void setDiscount(BigDecimal discount) {
this.discount = discount;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public BigDecimal getActualAmount() {
return actualAmount;
}
public void setActualAmount(BigDecimal actualAmount) {
this.actualAmount = actualAmount;
}
@Override
public String toString() {
return "Order{" +
"customer=" + customer +
", product='" + product + '\'' +
", num=" + num +
", price=" + price +
", discount=" + discount +
", amount=" + amount +
", actualAmount=" + actualAmount +
'}';
}
}
未使用策略模式
/**
* @author: tianjx
* @date: 2022/1/12 10:24
* @description: 行为型模式-策略模式
* 例子:客户下订单,通过不同的营销策略来打不同的折扣
*/
public class StragegyModeDemo {
public static void main(String[] args) {
Customer customerA = new Customer("1","A级别用户","A");
Customer customerB = new Customer("2","B级别用户","B");
Customer customerC = new Customer("3","C级别用户","C");
Order orderA = new Order(customerA,"鼠标", BigDecimal.valueOf(5),BigDecimal.valueOf(99));
Order orderB = new Order(customerB,"键盘", BigDecimal.valueOf(15),BigDecimal.valueOf(199));
Order orderC = new Order(customerC,"显示器", BigDecimal.valueOf(21),BigDecimal.valueOf(999));
// orderA.setDiscount(calcDiscount01(orderA.getCustomer().getCustomerCalss()));
// orderB.setDiscount(calcDiscount01(orderB.getCustomer().getCustomerCalss()));
// orderC.setDiscount(calcDiscount01(orderC.getCustomer().getCustomerCalss()));
orderA.setDiscount(calcDiscount02(orderA.getNum()));
orderB.setDiscount(calcDiscount02(orderB.getNum()));
orderC.setDiscount(calcDiscount02(orderC.getNum()));
orderA.setAmount(calcMultiply(orderA.getNum(),orderA.getPrice()));
orderB.setAmount(calcMultiply(orderB.getNum(),orderB.getPrice()));
orderC.setAmount(calcMultiply(orderC.getNum(),orderC.getPrice()));
orderA.setActualAmount(calcMultiply(orderA.getDiscount(),orderA.getAmount()));
orderB.setActualAmount(calcMultiply(orderB.getDiscount(),orderB.getAmount()));
orderC.setActualAmount(calcMultiply(orderC.getDiscount(),orderC.getAmount()));
System.out.println(orderA.toString());
System.out.println(orderB.toString());
System.out.println(orderC.toString());
}
// 计算折扣 根据用户级别来算折扣
private static BigDecimal calcDiscount01(String customerClass){
switch (customerClass){
case "A":
return BigDecimal.valueOf(0.9);
case "B":
return BigDecimal.valueOf(0.8);
case "C":
return BigDecimal.valueOf(0.75);
}
return BigDecimal.valueOf(0);
}
// 计算折扣 根据订单量来算折扣
private static BigDecimal calcDiscount02(BigDecimal num){
if (num.compareTo(BigDecimal.valueOf(10)) == -1) {
return BigDecimal.valueOf(0.9);
} else if (num.compareTo(BigDecimal.valueOf(20)) == -1) {
return BigDecimal.valueOf(0.8);
} else {
return BigDecimal.valueOf(0.75);
}
}
// 计算总原价
private static BigDecimal calcMultiply(BigDecimal one,BigDecimal two){
return one.abs().multiply(two.abs());
}
}
使用策略模式
定义策略模式接口
/**
* @author: tianjx
* @date: 2022/1/12 18:03
* @description: 策略模式接口
*/
public interface SalesStrategy {
BigDecimal calcDiscount(Order order);
}
定义策略模式实现类
/**
* @author: tianjx
* @date: 2022/1/12 18:04
* @description: 根据客户类别打折
*/
public class SalesStrategyByCustomerClass implements SalesStrategy{
@Override
public BigDecimal calcDiscount(Order order) {
switch (order.getCustomer().getCustomerCalss()){
case "A":
return BigDecimal.valueOf(0.9);
case "B":
return BigDecimal.valueOf(0.8);
case "C":
return BigDecimal.valueOf(0.75);
}
return BigDecimal.valueOf(0);
}
@Override
public String toString() {
return "SalesStrategyByCustomerClass{}";
}
}
/**
* @author: tianjx
* @date: 2022/1/12 18:06
* @description: 根据订单量来打折
*/
public class SalesStrategyByOrderNum implements SalesStrategy{
@Override
public BigDecimal calcDiscount(Order order) {
if (order.getNum().compareTo(BigDecimal.valueOf(10)) == -1) {
return BigDecimal.valueOf(0.9);
} else if (order.getNum().compareTo(BigDecimal.valueOf(20)) == -1) {
return BigDecimal.valueOf(0.8);
} else {
return BigDecimal.valueOf(0.75);
}
}
@Override
public String toString() {
return "SalesStrategyByOrderNum{}";
}
}
订单实体类添加策略模式
/**
* @author: tianjx
* @date: 2022/1/12 10:56
* @description: 订单类
*/
public class Order {
......
// 营销策略
private SalesStrategy salesStrategy;
public SalesStrategy getSalesStrategy() {
return salesStrategy;
}
// 设置营销策略,并设置折扣
public void setSalesStrategy(SalesStrategy salesStrategy) {
this.salesStrategy = salesStrategy;
setDiscount(salesStrategy.calcDiscount(this));
}
......
}
测试调用
/**
* @author: tianjx
* @date: 2022/1/12 18:08
* @description:
*/
public class StragegyModeDemo02 {
public static void main(String[] args) {
Customer customerA = new Customer("1","A级别用户","A");
Customer customerB = new Customer("2","B级别用户","B");
Customer customerC = new Customer("3","C级别用户","C");
Order orderA = new Order(customerA,"鼠标", BigDecimal.valueOf(5),BigDecimal.valueOf(99));
Order orderB = new Order(customerB,"键盘", BigDecimal.valueOf(15),BigDecimal.valueOf(199));
Order orderC = new Order(customerC,"显示器", BigDecimal.valueOf(21),BigDecimal.valueOf(999));
orderA.setSalesStrategy(new SalesStrategyByCustomerClass());
orderB.setSalesStrategy(new SalesStrategyByOrderNum());
orderC.setSalesStrategy(new SalesStrategyByOrderNum());
orderA.setAmount(calcMultiply(orderA.getNum(),orderA.getPrice()));
orderB.setAmount(calcMultiply(orderB.getNum(),orderB.getPrice()));
orderC.setAmount(calcMultiply(orderC.getNum(),orderC.getPrice()));
orderA.setActualAmount(calcMultiply(orderA.getDiscount(),orderA.getAmount()));
orderB.setActualAmount(calcMultiply(orderB.getDiscount(),orderB.getAmount()));
orderC.setActualAmount(calcMultiply(orderC.getDiscount(),orderC.getAmount()));
System.out.println(orderA.toString());
System.out.println(orderB.toString());
System.out.println(orderC.toString());
}
// 计算总原价
private static BigDecimal calcMultiply(BigDecimal one,BigDecimal two){
return one.abs().multiply(two.abs());
}
}
可以看出,如果没有使用策略的话,当有新的营销策略的时候,还得再写方法,修改原来的代码,如果使用了策略模式,我们只需要在定一个新的类就可以了,改一下策略模式就行了!首先非常符合开闭原则,而且更加不会让我们的代码显的臃肿!
在来一个例子,在换一种写法,在加深一下印象!(在我这必须让大家理解明明白白的)
分析:目的是解析文件,但是文件有word,excel两种,那我们就定义一个策略接口,两种实现方式即可!
定义一个枚举
/**
* @author: tianjx
* @date: 2022/1/13 10:48
* @description:
*/
public enum FileTypeResolveType {
FILE_WORD_RESOLVE("FILE_WORD_RESOLVE","WORD文件类型"),
FILE_EXCEL_RESOLVE("FILE_EXCEL_RESOLVE","EXCLE文件类型"),
FILE_DEFAULT_RESOLVE("FILE_DEFAULT_RESOLVE","默认文件类型");
FileTypeResolveType(String type, String content) {}
}
定义策略接口
/**
* @author: tianjx
* @date: 2022/1/13 10:52
* @description: 文件解析接口
*/
public interface IFileStrategy {
// 文件解析类型
FileTypeResolveType gainFileType();
// 子类实现的具体解析方式
void resolve(Object params);
}
定义策略实现类
/**
* @author: tianjx
* @date: 2022/1/13 10:55
* @description:
*/
@Component
public class ExcelFileResolve implements IFileStrategy{
@Override
public FileTypeResolveType gainFileType() {
return FileTypeResolveType.FILE_EXCEL_RESOLVE;
}
@Override
public void resolve(Object params) {
System.out.println("解析EXCEL文件!!!");
}
}
/**
* @author: tianjx
* @date: 2022/1/13 10:53
* @description:
*/
@Component
public class WordFileResolve implements IFileStrategy{
@Override
public FileTypeResolveType gainFileType() {
return FileTypeResolveType.FILE_WORD_RESOLVE;
}
@Override
public void resolve(Object params) {
System.out.println("解析word文件!!");
}
}
/**
* @author: tianjx
* @date: 2022/1/13 10:56
* @description:
*/
@Component
public class DefaultFileResolve implements IFileStrategy{
@Override
public FileTypeResolveType gainFileType() {
return FileTypeResolveType.FILE_DEFAULT_RESOLVE;
}
@Override
public void resolve(Object params) {
System.out.println("默认文件解析类型!!!");
}
}
与spring整合,继承ApplicationContextAware
@Component
public class StragetyUserService implements ApplicationContextAware {
private Map<FileTypeResolveType,IFileStrategy> iFileStrategyMap = new ConcurrentHashMap<>();
// 对外提供的方法,调用就行
public void resolveFile(FileTypeResolveType fileTypeResolveType,Object params){
IFileStrategy iFileStrategy = iFileStrategyMap.get(fileTypeResolveType);
if (iFileStrategy != null){
iFileStrategy.resolve(params);
}
}
// 把所有的策略放到 iFileStrategyMap 中
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, IFileStrategy> tmepMap = applicationContext.getBeansOfType(IFileStrategy.class);
tmepMap.values().forEach(strategyService -> {
iFileStrategyMap.put(strategyService.gainFileType(), strategyService);
});
}
}
测试
@SpringBootTest
class DemoApplicationTests {
@Autowired
StragetyUserService stragetyUserService;
@Test
void contextLoads() {
stragetyUserService.resolveFile(FileTypeResolveType.FILE_WORD_RESOLVE, "asd");
stragetyUserService.resolveFile(FileTypeResolveType.FILE_EXCEL_RESOLVE, "asd");
stragetyUserService.resolveFile(FileTypeResolveType.FILE_DEFAULT_RESOLVE, "asd");
}
}
有没有感受到代码的优雅,首先不同策略有不同的实现方式,符合单一职责!其次如果添加新的策略,只需添加相应的实现类即可!非常的方便,所以我们的代码还是非常的优雅的!
来个小总结呗~
策略模式就是相同的功能上,有不同的策略!一般适用于if...elseif...else
的场景!
不过可能也有人认为每次都添加一个类很麻烦,这个就是适当应用,其实根据上面的两个例子来讲,策略模式也只是一种让代码优雅的思想,让我们再写代码的时候多了一种方式方法!选不选用还是看具体情况!
感谢大家的阅读,我是Alson_Code🆒,一个喜欢把简单问题复杂化,把复杂问题简单化的程序猿! ❤