策略模式的实现

1. 引言

代码的高可维护性和扩展性一直是软件工程师的极致追求。试想一下,当我们负责开发一个系统时,可能会需要根据不同的用户职责和使用场景,动态地调整不同的行为策略。而随着系统的不断迭代更新,就会发现原有的代码开始变得异常臃肿,难以维护,如果后续新开了职位,就不得不修改大量的现有代码,这将极大的增加出错的风险,也降低了开发效率。

针对这种情况,这时候就需要策略模式大显身手了。策略模式是一种行为设计模式,它定义了一系列算法,并将每一个算法封装起来,使它们可以互换。这种模式让算法的变化独立于使用算法的客户端。通过使用策略模式,我们可以轻松地添加或修改策略,而不需要修改现有的代码。这不仅提高了代码的可维护性,还使得系统更加灵活和可扩展。

当然,策略模式的好处远不止于此。下面我们将会详细介绍策略模式的基本概念、实现步骤。通过本篇文章,你将能够理解策略模式的工作原理,并学会如何在实际项目中有效地利用它。

2. 何为策略模式

可以打这么个比方,策略模式就像是在餐厅点餐的过程。假设你和朋友一起去餐厅,你们可以根据自己的口味选择不同的菜肴。你可以点披萨,你的朋友可以点汉堡,而另一位朋友可以点沙拉。每个人都可以根据自己的喜好选择不同的策略(菜肴),但最终都能满足吃东西的需求。

huangbao.png

2.1 策略模式的主要角色

在这个过程中,餐厅的菜单(策略接口)和具体的菜肴(具体策略类)是固定的,但你可以根据自己的喜好(上下文)选择不同的菜肴。这就是策略模式的核心思想:在运行时选择不同的算法或行为,而不需要改变使用这些算法或行为的代码:

  1. 策略接口(Strategy Interface)  定义一个公共接口,所有具体的策略类都必须实现这个接口。就像是菜单上的菜肴类别,比如“主菜”、“甜点”等。它定义了你可以在餐厅点的东西的类型。
  2. 具体策略类(Concrete Strategy)  实现了策略接口,提供具体的算法实现。就像是菜单上具体的菜肴,比如“意大利披萨”、“牛肉汉堡”等。每种菜肴都有自己独特的做法和口味,但它们都实现了“主菜”这个策略接口。
  3. 上下文类(Context)  持有一个策略对象的引用,并提供一个方法来设置和使用这个策略对象。就像是服务员。当你点餐时,服务员会记录你点的菜肴,并确保它被正确地送到厨房制作。服务员并不关心菜肴的具体做法,他只是负责传递你的点餐需求。

2.2 策略模式的优点

通过这种方式,策略模式使得代码更加灵活和可扩展,就像在餐厅点餐一样,可以随时选择不同的菜肴,而不需要改变餐厅的菜单或服务流程。如同策略模式的优点一般:

  1. 算法封装:每个算法都被封装在一个独立的策略类中,便于维护和扩展。
  2. 易于切换算法:可以在运行时动态切换算法,而不需要修改客户端代码。
  3. 避免条件语句:策略模式可以避免使用大量的条件语句来选择不同的算法。

2.3 策略模式的缺点

当然,如果点餐时菜单上的菜肴太多,也会让人陷入应接不暇的窘境,且需要从中筛选出我们真实想要的菜肴类别,才能点到我们预期的菜品。就如策略模式的缺点一样:

  1. 增加类的数量:引入策略模式会增加类的数量,可能会增加系统的复杂性。
  2. 客户端需要了解策略:客户端需要了解不同的策略类,并选择合适的策略。

3. 策略模式的简单实现

以餐厅点餐为例,假设我们有一个餐厅点餐系统,顾客可以选择不同的菜肴,并且系统可以根据顾客的选择来准备相应的菜肴。

  1. 首先,我们需要定义一个策略接口,其包含一个方法,用来提供不同的菜肴。
 

java

代码解读

复制代码

public interface CookingStrategy { void cook(); }

  1. 接下来,我们来实现具体的策略类,每个类代表一种不同的菜肴。
 

java

代码解读

复制代码

public class PizzaCookingStrategy implements CookingStrategy { @Override public void cook() { System.out.println("可口的意大利披萨"); } }

 

java

代码解读

复制代码

public class BurgerCookingStrategy implements CookingStrategy { @Override public void cook() { System.out.println("美味的蟹堡王汉堡"); } }

 

java

代码解读

复制代码

public class SaladCookingStrategy implements CookingStrategy { @Override public void cook() { System.out.println("清爽的蔬菜沙拉"); } }

  1. 然后,定义一个上下文类,让它持有CookingStrategy对象,并提供一个方法来设置和使用这个策略对象。
 

java

代码解读

复制代码

public class Order { private CookingStrategy cookingStrategy; public void setCookingStrategy(CookingStrategy cookingStrategy) { this.cookingStrategy = cookingStrategy; } public void prepareDish() { if (cookingStrategy != null) { cookingStrategy.cook(); } else { System.out.println("请选择烹饪策略"); } } }

  1. 最后,在客户端编写代码模拟顾客点餐的过程。
 

java

整理了这份面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题

需要全套面试笔记【点击此处】即可免费获取

代码解读

复制代码

public class Restaurant { public static void main(String[] args) { Order order = new Order(); // 顾客点了一份披萨 CookingStrategy pizzaStrategy = new PizzaCookingStrategy(); order.setCookingStrategy(pizzaStrategy); order.prepareDish(); // 顾客又点了一份汉堡 CookingStrategy burgerStrategy = new BurgerCookingStrategy(); order.setCookingStrategy(burgerStrategy); order.prepareDish(); // 顾客最后点了一份沙拉 CookingStrategy saladStrategy = new SaladCookingStrategy(); order.setCookingStrategy(saladStrategy); order.prepareDish(); } }

4. 策略模式在SpringBoot框架中的应用

学过SpringBoot的都知道,SpringBoot是一个非常庞大的框架,使用了多种设计模式,其中就包含了策略模式。我们可以简单的看一段SpringBoot框架中策略模式的使用。

org.springframework.boot.env.EnvironmentPostProcessor中,提供了一个策略接口EnvironmentPostProcessor,它的主要作用是在应用上下文刷新之前对Environment进行自定义处理。多个EnvironmentPostProcessor实现类可以被注册到SpringBoot应用中,每个实现类都可以对Environment进行不同的处理。

源码如下:

  1. 策略接口
 

java

代码解读

复制代码

package org.springframework.boot.env; import org.springframework.boot.SpringApplication; import org.springframework.core.env.ConfigurableEnvironment; @FunctionalInterface public interface EnvironmentPostProcessor { void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application); }

  1. SpringBoot 提供了多个EnvironmentPostProcessor的具体策略类,如SpringApplicationJsonEnvironmentPostProcessor 等。
 

java

代码解读

复制代码

package org.springframework.boot.env; public class SpringApplicationJsonEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { MutablePropertySources propertySources = environment.getPropertySources(); propertySources.stream().map(JsonPropertyValue::get).filter(Objects::nonNull).findFirst().ifPresent((v) -> { this.processJson(environment, v); }); } }

  1. SpringBoot 的 SpringApplication 类充当了上下文角色,它在启动过程中会加载并调用所有注册的 EnvironmentPostProcessor 实现类。
 

java

代码解读

复制代码

package org.springframework.boot; public class SpringApplication { private List<EnvironmentPostProcessor> environmentPostProcessors = new ArrayList<>(); public void setEnvironmentPostProcessors(List<EnvironmentPostProcessor> environmentPostProcessors) { this.environmentPostProcessors = environmentPostProcessors; } public void run(String... args) { ConfigurableEnvironment environment = prepareEnvironment(args); for (EnvironmentPostProcessor postProcessor : environmentPostProcessors) { postProcessor.postProcessEnvironment(environment, this); } refreshContext(context); } }

在这个例子中,EnvironmentPostProcessor 接口和它的实现类(如SpringApplicationJsonEnvironmentPostProcessor)是策略模式中的具体策略类。SpringApplication 类是上下文类,它持有一组 EnvironmentPostProcessor 实现类,并在启动过程中根据需要调用这些实现类。这种设计体现了策略模式的核心思想,即在运行时选择不同的算法或行为,而不需要改变使用这些算法或行为的代码。

4.1 使用依赖注入和Spring的配置实现策略模式

在Spring Boot中使用策略模式,可以通过依赖注入和Spring的配置来实现。让我们以餐厅点餐系统为例,展示如何在Spring Boot中应用策略模式。

假设我们有一个餐厅点餐系统,顾客可以选择不同的菜肴(策略),并且系统可以根据顾客的选择来准备相应的菜肴。

1. 定义策略接口

首先,我们定义一个策略接口 CookingStrategy,它包含一个 cook 方法,用于准备菜肴。

 

java

代码解读

复制代码

public interface CookingStrategy { void cook(); }

2. 实现具体策略类

接下来,我们实现具体的策略类,每个类代表一种不同的菜肴。

 

java

代码解读

复制代码

import org.springframework.stereotype.Component; @Component("pizzaCookingStrategy") public class PizzaCookingStrategy implements CookingStrategy { @Override public void cook() { System.out.println("可口的意大利披萨"); } }

 

java

代码解读

复制代码

import org.springframework.stereotype.Component; @Component("burgerCookingStrategy") public class BurgerCookingStrategy implements CookingStrategy { @Override public void cook() { System.out.println("美味的蟹堡王汉堡"); } }

3. 定义上下文类

然后,我们定义一个上下文类 Order,它持有一个 CookingStrategy 对象,并提供一个方法来设置和使用这个策略对象。

 

java

代码解读

复制代码

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; @Service public class Order { private CookingStrategy cookingStrategy; @Autowired public void setCookingStrategy(@Qualifier("pizzaCookingStrategy") CookingStrategy cookingStrategy) { this.cookingStrategy = cookingStrategy; } public void prepareDish() { if (cookingStrategy != null) { cookingStrategy.cook(); } else { System.out.println("请选择烹饪策略"); } } }

4. 配置Spring Boot应用

在Spring Boot应用的主类中,启用组件扫描。

 

java

代码解读

复制代码

import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class RestaurantApplication { public static void main(String[] args) { SpringApplication.run(RestaurantApplication.class, args); } }

5. 客户端代码

最后,我们编写客户端代码来模拟顾客点餐的过程。

 

java

代码解读

复制代码

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class RestaurantRunner implements CommandLineRunner { @Autowired private Order order; @Override public void run(String... args) throws Exception { order.prepareDish(); } }

5. 总结

通过策略模式,我们可以轻松地添加新的菜肴(策略),而不需要修改现有的代码。这使得系统更加灵活和易于扩展。顾客可以根据自己的喜好选择不同的菜肴,而餐厅的菜单(策略接口)和具体的菜肴(具体策略类)是固定的,但顾客可以根据自己的喜好(上下文)选择不同的菜肴。 这就是策略模式的核心思想:在运行时选择不同的算法或行为,而不需要改变使用这些算法或行为的代码。

  • 27
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值