我是一个标题党
最近想输出记录点啥玩意,想着就从设计模式开始吧,废话不多说。
最近项目中使用到一些库存管理的策略,引入了设计模式中的策略模式,之前做订单的时候也引入了,所以相对而言也更熟悉一点,就拿这个开始吧;
什么是策略模式
策略模式是一种行为设计模式, 它能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换。 这是官方的说明,其实已经说的很明了,就是将算法封装,又能够相互替换
比如你要去前往机场。 你可以选择乘坐公共汽车、 打车或骑共享单子。 这些就是你的出行策略。
把出行方式封装起来,那我们的选择只根据你的选择因素,你的钱包和时间来决定你选择哪一种策略。
策略模式结构
我直接找了一张图,省的我自己画了
想必大家也能看得明白,我就简单说明一下,策略模式中,一般维护三种角色
- Context(上下文):维护策略对象的引用,就是将你的策略对象放到上下文中,通过接口可以切换到对象
- Strategy(策略接口):这里就是定义策略接口或者抽象类,比如上面的栗子,就是出现方式
- ConcreteStrategy(具体策略类):就是实现策略具体的算法
通过这种结构,我们调用时就可以根据具体需求选择不同策略,达到动态切换和扩展的目的了
伪代码
这边使用一个库存分配策略来演示,需求是要根据我们事先配置好的库存策略来进行库存的管理,目前的策略有2种,先进先出、先到期先出,后面也可能新增新的管理策略。根据这些我们开始
设计实现
定义枚举
typescript
/**
* 枚举类,分配策略
*
* @ClassName AllocateEnum
* @Author olding
*/
public enum AllocateEnum {
FIRST_IN_OUT(1,"先进先出","fifoAllocationStrategy"),
FIRST_TIME_OUT(2,"先到期先出","ftfoAllocationStrategy"),
;
private int type;
private String desc;
private String beanName;
AllocateEnum(int type, String desc, String beanName){
this.desc = desc;
this.type = type;
this.beanName = beanName;
}
public static AllocateEnum getEnumByType(int type){
for(AllocateEnum allocateEnum : AllocateEnum.values()){
if(Objects.equals(allocateEnum.getType(),type)){
return allocateEnum;
}
}
return null;
}
。。。
}
策略接口
csharp
/**
* 上下文会使用该接口来 调用有具体策略定义的算法。
* @description: 策略接口
* @author: olding
*/
public interface AllocationStrategy {
List<OutAllocation> doStrategy();
}
策略实现类
具体的细节 这里不一一实现了
kotlin
/**
* @description: 先进先出策略 FIFO
* @author: olding
*/
@Component
public class FifoAllocationStrategy implements AllocationStrategy{
/**
* 具体的实现算法
* 先进先出,按创建时间排序
* @return
*/
@Override
public List<OutAllocation> doStrategy() {
System.out.println("先进先出策略");
return null;
}
}
kotlin
/**
* @description: 先到期先出策略 ftfo
* @author: olding
*/
@Component
public class FtfoAllocationStrategy implements AllocationStrategy{
/**
* 具体的实现算法
* 先到期先出,按生产日期排序
* @return
*/
@Override
public List<OutAllocation> doStrategy() {
System.out.println("先到期先出策略");
return null;
}
}
上下文
这里使用实现Spring的接口ApplicationContextAware, 用于在bean初始化时获取ApplicationContext对象,在我实际调用具体策略的时候,根据我定义的枚举beanName 调用具体的实现策略
java
/**
* @author olding
*/
@Service
public class StrategyFactory implements ApplicationContextAware {
ApplicationContext applicationContext;
public AllocationStrategy getStrategy(Integer allocateType) throws Exception {
AllocateEnum allocateEnum = AllocateEnum.getEnumByType(allocateType);
if (allocateEnum == null || StringUtils.isBlank(allocateEnum.getBeanName())) {
throw new Exception("查询不到分配策略");
}
AllocationStrategy allocationStrategy;
allocationStrategy = applicationContext.getBean(allocateEnum.getBeanName(), AllocationStrategy.class);
return allocationStrategy;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
测试类
scala
/**
* @description:
* @author: olding
*/
@Slf4j
public class StrategyTest extends DemoApplicationTests {
@Autowired
StrategyFactory strategyFactory;
/**
* 客户端代码会选择具体策略并将其传递给上下文。客户端必须知晓策略之间的差
* 异,才能做出正确的选择。
*/
@Test
public void testGetStrategy() {
try {
AllocationStrategy allocationStrategy = strategyFactory.getStrategy(AllocateEnum.FIRST_IN_OUT.getType());
AllocationStrategy allocationStrategy2 = strategyFactory.getStrategy(AllocateEnum.FIRST_TIME_OUT.getType());
System.out.println(allocationStrategy.getClass());
System.out.println(allocationStrategy2.getClass());
// 执行分配策略,获取分配结果
allocationStrategy.doStrategy();
allocationStrategy2.doStrategy();
} catch (Exception e) {
log.error("execute out allocation exception:{}", e);
}
}
}
输出结果
kotlin
class com.olding.demo.strategies.FifoAllocationStrategy
class com.olding.demo.strategies.FtfoAllocationStrategy
先进先出策略
先到期先出策略
执行策略模式前,我们必须知道策略之间的具体差异,就需要根据一些条件来确定选择什么具体策略,可以根据配置定义,可以根据条件过滤等等方法都可以;
适合应用场景
- 当你想使用对象中各种不同的算法变体, 并希望能在运行时切换算法时, 可使用策略模式。
** 策略模式让你能够将对象关联至可以不同方式执行特定子任务的不同子对象, 从而以间接方式在运行时更改对象行为。
- 当你有许多仅在执行某些行为时略有不同的相似类时, 可使用策略模式。
** 策略模式让你能将不同行为抽取到一个独立类层次结构中, 并将原始类组合成同一个, 从而减少重复代码。
- 如果算法在上下文的逻辑中不是特别重要, 使用该模式能将类的业务逻辑与其算法实现细节隔离开来。
** 策略模式让你能将各种算法的代码、 内部数据和依赖关系与其他代码隔离开来。 不同客户端可通过一个简单接口执行算法, 并能在运行时进行切换。
- 当类中使用了复杂条件运算符以在同一算法的不同变体中切换时, 可使用该模式。
** 策略模式将所有继承自同样接口的算法抽取到独立类中, 因此不再需要条件语句。 原始对象并不实现所有算法的变体, 而是将执行工作委派给其中的一个独立算法对象。
比较常见的一些场景:
- 支付场景:多种支付方式,支付宝,微信,银行卡等等
- 路线选择场景:根据不同的条件和需求(如时间、费用、交通状况等),可以采用不同的路线选择策略
- 网站上的促销活动场景:满减、折扣、赠品等。可以根据不同的销售情况选择应用不同的促销策略
优缺点
优点
- 算法独立:显而易见,算法可以独立于客户端而变化,优化了大量的if
- 扩展性好:新增策略可以很方便,且不会影响原有代码
- 复用性高:不同策略之间是可以共享接口的,提高代码复用性
缺点
- 每个具体策略都需要一个单独的类,这可能导致类的数量增加
- 如果策略类过多,维护和管理起来可能会变得复杂
总结
使用好策略模式,可以让你的项目代码更加的整洁优雅,当然还是合理利用,前提是知晓他的优缺点,适不适合,我们使用设计模式是把复杂变得简单,而不是为了设计模式而设计,一味的套用反而适得其反。不要一味的炫技,可读性才是第一位。最后祝各位代码永无bug