设计模式-策略模式

1. 策略模式

策略模式(Strategy Pattern)针对一组算法,将每一个算法封装到 具有共同接口 的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。

在软件开发中,经常会遇到这种情况,开发一个功能可以通过多个算法去实现,比如:代付功能可以选择中金、ChinaPay、全渠道等。我们可以将所有的算法集中在一个类中,在这个类中提供多个方法,每个方法对应一个算法,或者我们也可以将这些算法都封装在一个统一的方法中,使用 if...else... 等条件判断语句进行选择。但是这两种方式都存在 硬编码 的问题,后期需要增加算法就需要修改源代码,这会导致代码的维护变得困难。

策略模式中可以定义一些独立的类来封装不同的算法,每一个类封装一种具体的算法,在这里每一个封装算法的类都可以被称为一种策略,为了保证这些策略在使用时具有一致性,一般会提供一个抽象策略类来做算法的声明。而每种算法对应一个具体策略类

策略模式主要目的是通过定义相似的算法,替换 if else 语句写法,并且可以随时相互替换。当实现某一个功能存在多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。

1.1 组成

  • 抽象策略Strategy):这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
  • 具体策略类(Concrete Strategy):实现了抽象策略定义的接口,提供具体的算法实现或行为。
  • 环境类Context):使用算法的角色,持有一个抽象策略引用,提供给客户端使用。

1.2 示例

使用计算器进行计算的时候,会经常用到加减乘除方法。如果我们想得到两个数字相加的和,我们需要用到"+"符号,得到相减的差,需要用到"-"符号等等。虽然我们可以通过字符串比较使用if/else写成通用方法,但是计算的符号每次增加,我们就不得不加在原先的方法中进行增加相应的代码,如果后续计算方法增加、修改或删除,那么会使后续的维护变得困难。

但是在这些方法中,我们发现其基本方法是固定的,这时我们就可以通过策略模式来进行开发,可以有效避免通过if/else来进行判断,即使后续增加其他的计算规则也可灵活进行调整。

1.2.1 抽象策略

java 复制代码
interface CalculateStrategy {
   int doOperation(int num1, int num2);
}

1.2.2 具体策略类

💗加法:

java 复制代码
class OperationAdd implements CalculateStrategy {
   @Override
   public int doOperation(int num1, int num2) {
      return num1 + num2;
   }
}

💙减法:

java 复制代码
class OperationSub implements CalculateStrategy {
   @Override
   public int doOperation(int num1, int num2) {
      return num1 - num2;
   }
}

💚乘法:

java 复制代码
class OperationMul implements CalculateStrategy {
   @Override
   public int doOperation(int num1, int num2) {
      return num1 * num2;
   }
}

💛除法:

java 复制代码
class OperationDiv implements CalculateStrategy {
   @Override
   public int doOperation(int num1, int num2) {
      return num1 / num2;
   }
}

1.2.3 环境类

定义一个环境角色,提供一个计算的接口供客户端使用。

java 复制代码
class  CalculatorContext {
    // 抽象策略的引用
	private CalculateStrategy strategy;

	public CalculatorContext(CalculateStrategy strategy) {
		this.strategy = strategy;
	}

    // 提供一个计算的接口供客户端使用
	public int executeStrategy(int num1, int num2) {
		return strategy.doOperation(num1, num2);
	}
}

1.2.4 测试

java 复制代码
public static void main(String[] args) {
	int a=4,b=2;
	CalculatorContext context = new CalculatorContext(new OperationAdd());    
	System.out.println("a + b = " + context.executeStrategy(a, b));
	
	context = new CalculatorContext(new OperationSub());      
	System.out.println("a - b = " + context.executeStrategy(a, b));
	
	context = new CalculatorContext(new OperationMul());    
	System.out.println("a * b = " + context.executeStrategy(a, b));
	
	context = new CalculatorContext(new OperationDiv());    
	System.out.println("a / b = " + context.executeStrategy(a, b));
}

📊输出结果:

java 复制代码
a + b = 6
a - b = 2
a * b = 8
a / b = 2

1.3 优缺点

  • 👍优点:
    1. 扩展性好,增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合"开闭原则"。
    2. 灵活性好,可以对算法进行自由切换。
    3. 避免使用多重条件选择语句(if else),充分体现面向对象设计思想。
  • 👎缺点:
    1. 使用策略类变多,会增加系统的复杂度。
    2. 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。

1.4 使用场景

  • 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。 策略模式最大的作用在于分离使用算法的逻辑和算法自身实现的逻辑,这样就意味着当我们想要优化算法自身的实现逻辑时就变得非常便捷,一方面可以采用最新的算法实现逻辑,另一方面可以直接弃用旧算法而采用新算法。使用策略模式能够很方便地进行替换。
  • 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。 在实际开发中,有许多算法可以实现某一功能,如查找、排序等,通过 if-else 等条件判断语句来进行选择非常方便。但是这就会带来一个问题:当在这个算法类中封装了大量查找算法时,该类的代码就会变得非常复杂,维护也会突然就变得非常困难。虽然策略模式看上去比较笨重,但实际上在每一次新增策略时都通过新增类来进行隔离,短期虽然不如直接写 if-else 来得效率高,但长期来看,维护单一的简单类耗费的时间其实远远低于维护一个超大的复杂类。
  • 系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。 如果我们不希望客户知道复杂的、与算法相关的数据结构,在具体策略类中封装算法与相关数据结构,可以提高算法的保密性与安全性。

1.5 模板方法模式和策略模式区别

这两种模式在 Spring 中都广泛存在,例如大家所熟知的 Spring Data 模块,存在 JDBCTemplateRedisTemplateMongoTemplate 等均是典型的 模板模式 。而例如 Spring MVC 中各种处理 handler,则是典型的 策略模式。两者有什么区别呢?

  • 模板方法模式 一般只针对一套算法,注重对同一个算法的不同细节进行抽象提供不同的实现。而 策略模式 注重多套算法多套实现,替换 if else 语句写法,并且可以随时替换算法

参考文章:

📖 策略模式和模板方法模式

📖 JAVA设计模式之策略模式详解

相关推荐
Chengweili12023 小时前
《设计模式》创建型模式总结
设计模式
ducking__3 小时前
设计模式练习(二) 简单工厂模式
c++·设计模式·简单工厂模式
捕鲸叉3 小时前
C++创建型设计模式综合示例
开发语言·c++·设计模式
G皮T3 小时前
【设计模式】行为型模式(三):责任链模式、状态模式
java·设计模式·状态模式·编程·责任链模式·state
捕鲸叉3 小时前
C++创建型设计模式体现出的面向对象设计原则
开发语言·c++·设计模式
吾与谁归in4 小时前
【C#设计模式(7)——桥接模式(Bridge Pattern)】
设计模式·c#·桥接模式
她说人狗殊途17 小时前
设计模式学习
java·学习·设计模式
GISer_Jing20 小时前
Javascript——设计模式(一)
前端·javascript·设计模式
八宝袋21 小时前
设计模式之工厂模式,但是宝可梦
java·设计模式·工厂模式