01-策略模式
简洁版

- 环境类(Context):它持有一个策略类的引用,通过调用策略类的方法来执行具体的算法。环境类提供了一个统一的接口,使得客户端可以方便地使用不同的策略。客户端直接与Context类交互,不直接与具体的策略类交互。
- 抽象策略类(Strategy):它定义了一个公共接口(也可以是抽象类),用于封装具体的算法。不同的具体策略类实现这个接口,提供了不同的算法实现。
- 具体策略类(Concrete Strategy):它实现了抽象策略类定义的算法接口,具体实现了具体的算法逻辑。
空心菱形箭头(聚合关系):从 Context
到 IStrategy
的箭头表示聚合关系(整体指向部分)。这是表示 Context
类中包含了一个 IStrategy
类型的成员变量 strategy
。也就是说,Context
类依赖于 IStrategy
接口的实例。
虚线带空心三角箭头(实现关系):从 ConcreteA
和 ConcreteB
到 IStrategy
的箭头表示实现关系(Realization)。这表示 ConcreteA
和 ConcreteB
类实现了 IStrategy
接口,即它们都提供了 algorithm()
方法的具体实现。
注册为0级用户,每天有3条免费消息使用额度,每天凌晨定时刷新Redis缓存,缓存淘汰策略使用最久未使用。连续签到7天升级到1级用户,连续签到1个月升级到2级用户,1级用户购买会员卡有0.95折扣,2级用户购买会员卡有0.9则扣,购买会员卡后升级到3级用户,3级用户续费可以0.8则优惠,0级用户原价购买。
购买方案使用策略模式。
Chat GPT
策略模式是一种行为设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以互换使用。策略模式使得算法可以在不影响客户端的情况下发生变化。
以下是策略模式的基本结构和使用步骤:
1. 定义策略接口
策略接口定义了所有具体策略类必须实现的方法。这些方法就是不同算法的通用接口。
public interface Strategy {
void execute();
}
2. 创建具体策略类
具体策略类实现了策略接口中的方法,代表具体的算法或行为。
public class ConcreteStrategyA implements Strategy {
@Override
public void execute() {
System.out.println("Executing Strategy A");
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void execute() {
System.out.println("Executing Strategy B");
}
}
3. 创建上下文类
上下文类持有一个策略对象,并且可以根据需要动态地设置或更改策略。上下文类调用策略对象的方法来执行具体的算法。
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void executeStrategy() {
strategy.execute();
}
}
4. 客户端代码
客户端代码创建具体的策略对象,并将其传递给上下文类来执行。
public class Client {
public static void main(String[] args) {
Context context = new Context(new ConcreteStrategyA());
context.executeStrategy(); // 输出:Executing Strategy A
context.setStrategy(new ConcreteStrategyB());
context.executeStrategy(); // 输出:Executing Strategy B
}
}
5.使用策略模式的优点
- 易于扩展:可以添加新的策略类,而无需修改上下文类和客户端代码。
- 避免条件判断:通过策略模式,可以避免在客户端代码中使用大量的条件语句来选择不同的算法。
- 灵活性:可以在运行时动态地改变策略,使得程序更具灵活性。
6.示例:折扣策略
假设我们有一个电商平台,根据不同的客户类型(比如普通客户、会员客户和VIP客户),需要使用不同的折扣策略。我们可以使用策略模式来实现这一点。
// 折扣策略接口
public interface DiscountStrategy {
double applyDiscount(double price);
}
// 普通客户折扣策略
public class RegularCustomerStrategy implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
return price;
}
}
// 会员客户折扣策略
public class MemberCustomerStrategy implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
return price * 0.9; // 10% 折扣
}
}
// VIP客户折扣策略
public class VIPCustomerStrategy implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
return price * 0.8; // 20% 折扣
}
}
// 上下文类
public class ShoppingCart {
private DiscountStrategy discountStrategy;
public ShoppingCart(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
public void setDiscountStrategy(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
public double calculateTotal(double price) {
return discountStrategy.applyDiscount(price);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart(new RegularCustomerStrategy());
double total = cart.calculateTotal(100);
System.out.println("Total price for regular customer: " + total); // 输出:100.0
cart.setDiscountStrategy(new MemberCustomerStrategy());
total = cart.calculateTotal(100);
System.out.println("Total price for member customer: " + total); // 输出:90.0
cart.setDiscountStrategy(new VIPCustomerStrategy());
total = cart.calculateTotal(100);
System.out.println("Total price for VIP customer: " + total); // 输出:80.0
}
}
CSDN-优惠方案
https://blog.csdn.net/yuiezt/article/details/140002514
1、解释这个图片
这张图展示了策略模式(Strategy Pattern),这是一种行为设计模式。它允许你定义一组算法,分别封装起来,并使它们可以互相替换。这样,算法可以独立于使用它的客户端而变化。
以下是图中每个组件的解释:
IStrategy 接口:这是一个接口,声明了一个叫 algorithm()
的方法,所有具体策略都必须实现这个方法。它表示所有策略的共同行为。
Context 类:
- 持有一个指向
IStrategy
接口的引用 (strategy
)。 - 构造函数
Context(IStrategy)
用于注入一个具体的策略。 algorithm()
方法将调用当前策略对象的algorithm()
方法。
ConcreteA 和 ConcreteB 类:
- 这两个类是具体的策略类,分别实现了
IStrategy
接口的algorithm()
方法。 - 这些类提供了不同的算法实现,可以在运行时被上下文类替换。
2、图片里面的箭头分别是什么意思
空心菱形箭头(聚合关系):从 Context
到 IStrategy
的箭头表示聚合关系(整体指向部分)。这是表示 Context
类中包含了一个 IStrategy
类型的成员变量 strategy
。也就是说,Context
类依赖于 IStrategy
接口的实例。
虚线带空心三角箭头(实现关系):从 ConcreteA
和 ConcreteB
到 IStrategy
的箭头表示实现关系(Realization)。这表示 ConcreteA
和 ConcreteB
类实现了 IStrategy
接口,即它们都提供了 algorithm()
方法的具体实现。
3、IStrategy 只能是接口吗?可以是抽象类吗
在策略模式中,IStrategy
通常被设计为接口,但它也可以是抽象类。选择使用接口还是抽象类,取决于具体需求和设计考虑。以下是接口和抽象类在策略模式中的比较:
3.1 使用接口
纯粹的策略定义:
- 接口通常用于定义一组方法而不包含任何实现。这样,每个具体的策略类都必须实现接口中定义的所有方法,从而提供自己的算法实现。
更灵活:
- 接口允许类实现多个接口,这对需要多重继承的场景非常有用,因为 Java 等语言不支持类的多重继承,但支持接口的多重实现。
更清晰的设计:
- 接口提供了一个更加清晰的设计,表示这些类具有相同的行为,而不关注它们的实现细节。
示例:
public interface IStrategy {
void algorithm();
}
public class ConcreteA implements IStrategy {
public void algorithm() {
// ConcreteA的算法实现
}
}
public class ConcreteB implements IStrategy {
public void algorithm() {
// ConcreteB的算法实现
}
}
3.2 使用抽象类
部分实现:
- 抽象类可以包含部分实现。你可以在抽象类中提供一些默认的行为,具体的策略类只需重写那些需要特定实现的方法。
共享代码:
- 如果多个策略类有一些共同的代码,可以将这些代码放在抽象类中,从而避免代码重复,提高代码复用性。
更多的功能:
- 抽象类不仅可以包含方法声明,还可以包含成员变量和已实现的方法。这使得抽象类可以提供更多的功能和灵活性。
示例:
public abstract class IStrategy {
abstract void algorithm();
// 可以添加共享的代码
public void commonMethod() {
// 共享的方法实现
}
}
public class ConcreteA extends IStrategy {
@Override
public void algorithm() {
// ConcreteA的算法实现
}
}
public class ConcreteB extends IStrategy {
@Override
public void algorithm() {
// ConcreteB的算法实现
}
}
3.3 总结
- 如果策略之间没有任何共享的代码或行为,实现非常独立且简单,使用接口会更加合适。
- 如果策略之间有共享的代码或行为,使用抽象类可以减少代码重复,提高代码的可维护性。
4、为什么要有Context 类,它的作用是什么
Context
类在策略模式中起到了桥梁作用,将客户端与具体的策略实现解耦,使得策略的选择和切换更加灵活,并且促进了代码的可维护性和可扩展性。
Context 类主要功能:
持有一个策略接口的引用:Context
类持有一个 IStrategy
类型的成员变量 strategy
。
通过构造函数注入策略:Context
类通过构造函数接受一个策略对象,并将其赋值给成员变量 strategy
。
提供设置策略的方法:setStrategy(IStrategy strategy)
方法允许在运行时更换策略。
执行策略方法:executeStrategy()
方法调用当前策略对象的 algorithm()
方法。
Context 类具体作用:
封装策略的选择和切换:Context类通过持有一个
IStrategy接口的引用,能够在运行时动态地选择和切换具体的策略(即
ConcreteA或
ConcreteB`)。这使得算法的变化对客户端代码透明,客户端无需了解具体的策略实现。
将算法的具体实现与客户端代码分离:Context
类使得算法的具体实现与客户端代码分离。客户端只需与 Context
类交互,通过 Context
类来调用算法,而无需直接依赖于具体的策略类。这提高了代码的可维护性和可扩展性。
简化客户端代码:Context
类对外提供了统一的接口来执行算法。客户端只需调用 Context
的 algorithm()
方法,无需关心具体的策略类及其内部实现细节。这简化了客户端代码,减少了耦合。
促进代码的重用和扩展:通过使用 Context
类,可以轻松地添加新的策略而不改变现有的客户端代码。只需新建一个实现 IStrategy
接口的策略类,并通过 Context
注入即可。这种设计提高了代码的重用性和扩展性。
管理与协调策略的使用:Context
类负责管理和协调策略的使用。在一些复杂的应用场景中,策略的选择可能依赖于运行时的条件或上下文信息。Context
类可以封装这种逻辑,根据具体的条件选择合适的策略。
5、Context 类一般怎么写
Context
类在策略模式中的主要作用是持有一个策略接口的引用,并通过这个引用来调用具体的策略方法。以下是一个 Context
类的典型实现示例,展示了如何依赖接口或抽象类的两种实现方式。
5.1 依赖接口的 Context
类
假设 IStrategy
是一个接口:
public interface IStrategy {
void algorithm();
}
public class ConcreteA implements IStrategy {
public void algorithm() {
System.out.println("ConcreteA的算法实现");
}
}
public class ConcreteB implements IStrategy {
public void algorithm() {
System.out.println("ConcreteB的算法实现");
}
}
public class Context {
private IStrategy strategy;
// 构造函数注入策略
public Context(IStrategy strategy) {
this.strategy = strategy;
}
// 设置策略的方法,可以在运行时切换策略
public void setStrategy(IStrategy strategy) {
this.strategy = strategy;
}
// 执行策略方法
public void executeStrategy() {
strategy.algorithm();
}
}
// 测试
public class Main {
public static void main(String[] args) {
// 使用ConcreteA策略
Context context = new Context(new ConcreteA());
context.executeStrategy();
// 切换到ConcreteB策略
context.setStrategy(new ConcreteB());
context.executeStrategy();
}
}
5.2 依赖抽象类的 Context
类
假设 IStrategy
是一个抽象类:
public abstract class IStrategy {
abstract void algorithm();
// 可以有共享的方法
public void commonMethod() {
System.out.println("共享方法实现");
}
}
public class ConcreteA extends IStrategy {
@Override
public void algorithm() {
System.out.println("ConcreteA的算法实现");
}
}
public class ConcreteB extends IStrategy {
@Override
public void algorithm() {
System.out.println("ConcreteB的算法实现");
}
}
public class Context {
private IStrategy strategy;
// 构造函数注入策略
public Context(IStrategy strategy) {
this.strategy = strategy;
}
// 设置策略的方法,可以在运行时切换策略
public void setStrategy(IStrategy strategy) {
this.strategy = strategy;
}
// 执行策略方法
public void executeStrategy() {
strategy.algorithm();
}
}
// 测试
public class Main {
public static void main(String[] args) {
// 使用ConcreteA策略
Context context = new Context(new ConcreteA());
context.executeStrategy();
// 切换到ConcreteB策略
context.setStrategy(new ConcreteB());
context.executeStrategy();
}
}
CSDN-支付方案
https://blog.csdn.net/qq_42665745/article/details/128296806
模拟支付宝、微信、银联支付。

支付策略及其实现类
/**
* @description:支付策略接口
*/
public interface PayStrategy {
/**
* 支付方法
* @param orderId 订单id
* @param amount 金额
* @return
*/
boolean pay(String orderId, Long amount);
}
/**
* @description:微信支付策略
*/
public class WxPay implements PayStrategy{
@Override
public boolean pay(String orderId, Long amount) {
System.out.println("调用微信支付策略-->订单id:"+orderId+" ,金额:"+amount);
return true;
}
}
/**
* @description:支付宝支付策略
*/
public class ZfbPay implements PayStrategy{
@Override
public boolean pay(String orderId, Long amount) {
System.out.println("调用支付宝支付策略-->订单id:"+orderId+" ,金额:"+amount);
return true;
}
}
/**
* @description:银联支付策略
*/
public class YlPay implements PayStrategy{
@Override
public boolean pay(String orderId, Long amount) {
System.out.println("调用银联支付策略-->订单id:"+orderId+" ,金额:"+amount);
return true;
}
}
支付上下文类
/**
* @description:支付上下文类
*/
public class PayContext {
/**
* 订单id
*/
private String orderId;
/**
* 金额
*/
private Long amount;
private PayStrategy payStrategy;
public PayContext(String orderId, Long amount, PayStrategy payStrategy) {
this.orderId = orderId;
this.amount = amount;
this.payStrategy = payStrategy;
}
public void payContext(){
//调用具体策略的支付
this.payStrategy.pay(orderId,amount);
}
}
客户端测试类
public class Client {
public static void main(String[] args) {
//微信支付
new PayContext("10001",100L,new WxPay()).payContext();
//支付宝支付
new PayContext("10002",200L,new ZfbPay()).payContext();
//银联支付
new PayContext("10003",300L,new YlPay()).payContext();
}
}

看起来好像没有上下文什么事情,但是如果没有上下文,那么就需要客户端来直接与具体的策略交互,尤其是当需要提供一些公共功能,或者是相关状态存储的时候,会大大增加客户端使用的难度。因此,引入上下文还是很必要的,有了上下文,这些工作就由上下文来完成了,客户端只需要与上下文交互就可以了,这样会让整个设计模式更独立、更有整体性,也让客户端更简单。举例:支付案例参数不一致问题。
支付案例参数不一致问题
实际使用各种支付时,各自的参数必然不一样,但是策略又是接口定义好的,那应该怎么办呢,这时介于客户端和策略方法中间的上下文类就发挥作用了,可以在这里面搞点小动作,把微信和支付宝用到的参数声明在这里,然后把自己传给策略类,不同的策略类需要什么参数就取什么参数。
策略类及其实现类
/**
* @description:支付策略接口
*/
public interface PayStrategy {
/**
* 支付方法
* @return
*/
boolean pay(PayContext payContext);
}
/**
* @description:微信支付策略
*/
public class WxPay implements PayStrategy{
@Override
public boolean pay(PayContext payContext) {
System.out.println("微信支付参数:"+payContext.getWxAppserect());
System.out.println("调用微信支付策略-->订单id:"+payContext.getOrderId()+" ,金额:"+payContext.getAmount());
return true;
}
}
/**
* @description:支付宝支付策略
*/
public class ZfbPay implements PayStrategy{
@Override
public boolean pay(PayContext payContext) {
System.out.println("支付宝支付参数:"+payContext.getZfbId());
System.out.println("调用支付宝支付策略-->订单id:"+payContext.getOrderId()+" ,金额:"+payContext.getAmount());
return true;
}
}
/**
* @description:银联支付策略
*/
public class YlPay implements PayStrategy{
@Override
public boolean pay(PayContext payContext) {
System.out.println("银联支付没有参数");
System.out.println("调用银联支付策略-->订单id:"+payContext.getOrderId()+" ,金额:"+payContext.getAmount());
return true;
}
}
支付上下文类
/**
* @description:支付上下文类
*/
@Getter
public class PayContext {
/**
* 订单id
*/
private String orderId;
/**
* 金额
*/
private Long amount;
/**
* 微信支付需要的参数
*/
private String wxAppserect;
/**
* 支付宝支付需要的参数
*/
private String zfbId;
private PayStrategy payStrategy;
public PayContext(String orderId, Long amount, String wxAppserect, String zfbId, PayStrategy payStrategy) {
this.orderId = orderId;
this.amount = amount;
this.wxAppserect = wxAppserect;
this.zfbId = zfbId;
this.payStrategy = payStrategy;
}
public void payContext(){
//调用具体策略的支付
this.payStrategy.pay(this);
}
}
客户端测试类
public class Client {
public static void main(String[] args) {
//微信支付
PayContext wxPay = new PayContext("10001", 100L, "wx123", "", new WxPay());
wxPay.payContext();
//支付宝支付
PayContext zfbPay = new PayContext("10001", 100L, "", "zfb123", new ZfbPay());
zfbPay.payContext();
//银联支付
PayContext ylPay = new PayContext("10001", 100L, "", "", new YlPay());
ylPay.payContext();
}
}
