[设计模式] - 策略模式

一、策略模式简介

1. 什么是策略模式

策略模式(Strategy Pattern) 也叫作**政策模式(Policy)**是指定义了算法组并分别封装起来,让他们可以互相替换使得算法的更改并不会影响使用了算法的用户。策略模式属于行为性设计模式,在策略模式中,一个类的行为及算法可以在运行时更改。

2. 什么时候可以使用策略模式

策略模式通常应用在一下场景:

  1. 消除if-else 引起的代码复杂度提升和维护问题
  2. 应用在多个类之间只有算法和行为稍有不同的情况
  3. 需要屏蔽算法实现的场景
  4. 算法之间可以自由切换

二、业务场景分析策略模式

1. 业务场景

我觉得一些理论改变并不能很好的帮助大家理解设计模式的概念,相反容易催眠大家,所以在讲设计模式的时候我习惯用实际的业务场景来讲一下这个东西的应用和实现。今天我们就用一个策略模式最常见的使用场景:促销活动来看下怎么策略模式的应用。

某知名生鲜平台临近过年举办了年底促销活动,其中肉食类商品满100减20元,蔬菜类商品满50减15,熟食类商品满100减10元。小龙同学了解到促销互动后来到生鲜平台下单了123元的肉食类商品,53元的蔬菜类商品及140元的熟食类商品,请计算小龙同学最后需要支付多少钱?

2. 不采用策略模式的业务实现

如果不采用策略模式,我们可能第一时间想到的是通过简单的if-else来区分商品种类,然后计算优惠金额。那么我们来看下如何通过以前的方式进行实现并且分析下这段代码的优缺点:

第一步: 创建订单类接口

public interface Order {
    
    
// 计算优惠
	Long calculate();
}

第二步: 创建肉食,蔬菜,熟食订单类

public class MeatOrder implements Order{
    
    

	// 订单总金额
	private Long totalAmount;

	// 实际支付金额
	private Long realAmount;

	public Long calculate() {
    
    
		if (totalAmount >= 10_000) {
    
    
			realAmount = totalAmount - 2000;
		} else {
    
    
			realAmount = totalAmount;
		}
		return realAmount;
	}

	public MeatOrder(Long totalAmount) {
    
    
		super();
		this.totalAmount = totalAmount;
	}
}

public class VegetablesOrder implements Order {
    
    

	// 订单总金额
	private Long totalAmount;

	// 实际支付金额
	private Long realAmount;

	public Long calculate() {
    
    
		if (totalAmount >= 5000) {
    
    
			realAmount = totalAmount - 1500;
		} else {
    
    
			realAmount = totalAmount;
		}
		return realAmount;

	}

	public VegetablesOrder(Long totalAmount) {
    
    
		super();
		this.totalAmount = totalAmount;
	}

}
// 熟食订单
public class CookedFoodOrder implements Order {
    
    

	// 订单总金额
	private Long totalAmount;

	// 实际支付金额
	private Long realAmount;

	public Long calculate() {
    
    
		if (totalAmount >= 10_000) {
    
    
			realAmount = totalAmount - 1000;
		} else {
    
    
			realAmount = totalAmount;
		}
		return realAmount;

	}

	public CookedFoodOrder(Long totalAmount) {
    
    
		super();
		this.totalAmount = totalAmount;
	}

}

第三步: 创建收银台

public class Cashier {
    
    

	public static void main(String[] args) {
    
    
		MeatOrder meatOrder = new MeatOrder(123 * 100l);
		VegetablesOrder vegetablesOrder = new VegetablesOrder(53 * 100l);
		CookedFoodOrder cookedFoodOrder = new CookedFoodOrder(140 * 100l);
		// 收银
		List<Order> list = new ArrayList<>();
		list.add(meatOrder);
		list.add(vegetablesOrder);
		list.add(cookedFoodOrder);
		Long cashier = cashier(list);
		System.out.println("您一共消费"+(cashier / 100) + "元");

	}

	/**
	 * @Title: cashier
	 * @Description: TODO
	 *
	 */
	private static Long cashier(List<Order> list) {
    
    
		Long amount = 0l;
		for (Order order : list) {
    
    
			Long calculate = order.calculate();
			amount += calculate;
		}
		return amount;
	}
}

我们来分析这段代码的优缺点:
首先我们可以看出这段代码利用了Java自身多态的特性在每个订单类中实现了各自的满减算法。这种写法的优点首先是实现简单,很多同学都可以想得到。第二就是代码量较小,开发工作量并不大。那么这种写法有没有缺点呢?当然!这种写法的动态拓展能力很差,如果此时我们提出新的需求:

过完元旦,生鲜平台决定恢复原价

那我们思考一下这时候我们要怎么办呢?
在这里插入图片描述
最简单的办法肯定是直接将上面的代码中关于满减的部分删除掉。但是这无疑是违反了JAVA设计规范中的开闭原则。我们在针对业务变更的情况下应该是对拓展开放,对更改关闭,不允许直接修改代码。那么我们面对这种情况应该怎么做?

3. 策略模式实现

策略模式就是针对这种业务场景经常会发生变更的情况下设计的。这里我先提供一个策略模式代码实现,稍后在分析这段代码是如何设计的。

3.1 第一步 创建策略定义接口

public interface FullReduction {
    
    
	Long reduction(Long amount);
}

3.2 创建策略实现接口

public class MeatReduction implements FullReduction {
    
    

	@Override
	public Long reduction(Long amount) {
    
    
		if (amount >= 10_000) {
    
    
			return amount - 2000;
		} else {
    
    
			return amount;
		}
	}

}
public class CookedFoodReduction implements FullReduction {
    
    

	@Override
	public Long reduction(Long amount) {
    
    
		if (amount >= 10_000) {
    
    
			return amount - 1000;
		} else {
    
    
			return amount;
		}
	}

}

3.3 创建策略使用类

public class Order {
    
    

	private FullReduction reduction;

	private Long amount;

	public Order(FullReduction reduction, Long amount) {
    
    
		super();
		this.reduction = reduction;
		this.amount = amount;
	}

	public Long count() {
    
    
		return reduction.reduction(amount);
	}

}

3.4 创建收银台

public class Cashier {
    
    

	public static void main(String[] args) {
    
    
		List<Order> list = new ArrayList<Order>();
		list.add(new Order(new MeatReduction(), 124 * 100l));
		list.add(new Order(new CookedFoodReduction(), 140 * 100l));
		list.add(new Order(new VegetablesReduction(), 53 * 100l));
		Long amount = count(list);
		System.out.println("您消费总金额为:" + (amount / 100) + "元");
	}

	/**
	 * @Title: count
	 * @Description: TODO
	 *
	 */
	private static Long count(List<Order> list) {
    
    
		Long amount = 0l;
		for (Order order : list) {
    
    
			amount += order.count();
		}
		return amount;
	}
}

运行结果:
在这里插入图片描述

4. 通过业务分析策略模式的优缺点

我们通过比较上述代码可以简单的分析下策略模式的一些特点;
优点:

  1. 策略模式可以替代继承关系,通过聚合减少继承。
  2. 策略模式可以减少冗余的if-else判断代码块。通过算法决策使用对应算法即可。
  3. 策略模式符合开闭原则的要求,针对拓展开放,新增策略不会影响已有策略。

缺点:

  1. 客户端必须知道所有的策略并决定使用哪一种策略。
  2. 策略模式会产生很多策略类,增加了对象数量。

三、Java中的策略模式

1. 策略模式的设计思路

如果你看了上面的代码还是有疑问我觉得是很正常的,因为上述策略模式虽然提供了代码,但是整个模式的设计思路并没有详细的去讲。这里先画一个策略模式的组成图来让大家进一步的了解策略模式。

在这里插入图片描述
其实策略模式很简单的:

  1. 第一步:先创建一个策略的约束接口,约束想要的策略需要由哪些行为组成。
  2. 第二步:创建策略接口的实现类,每新增一个策略就新增一个实现类。
  3. 第三步:创建一个引用策略的Context选择组合器,用来实现策略和业务的组合。
  4. 第四步:在程序中指定业务所需要的策略即可。

2. JDK中的策略模式

2.1 Comparator接口

在java中最典型的策略接口的实现就是Comparator接口,Comparator接口中的sort方法针对不同类型的对象提供了不同的排序策略。如果我们想要实现一个自定义对象的排序,就需要针对自定义对象传递一个排序规则,既Comparator接口的实现。这里我贴出一段Arrays实现comparator中sort方法的源码供小伙伴们分析。
在这里插入图片描述

四、总结

1. 策略模式的特点:

优点:

  1. 策略模式可以替代继承关系,通过聚合减少继承。
  2. 策略模式可以减少冗余的if-else判断代码块。通过算法决策使用对应算法即可。
  3. 策略模式符合开闭原则的要求,针对拓展开放,新增策略不会影响已有策略。

缺点:

  1. 客户端必须知道所有的策略并决定使用哪一种策略。
  2. 策略模式会产生很多策略类,增加了对象数量。

2. 策略模式的使用场景

  1. 多个实体间仅有行为不同
  2. 一个系统需要动态的在多种算法中进行选择。

3. 策略模式解决的问题

1. 解除耦合: 策略模式完美的支持了“开闭原则”,用户可以在不修改原有基础系统的基础上选择实现方式,也可以灵活增加新的实现。简单说就是一个算法的增加改动并不会影响其他算法。
2. 有效的实现类管理: 提供了多种相同接口的实现的管理办法。由于存在一个策略管理类的角色,具体策略角色调用方式集中。
3. 替换继承关系: 提供了一种可以替换继承关系的办法。
4. 替换多重条件转移: 避免了使用多重条件转移语句。

好了,今天的内容到此为止,希望有收获的小伙伴可以一键三连!拒绝白嫖从我做起。

猜你喜欢

转载自blog.csdn.net/xiaoai1994/article/details/112266997