【软件设计模式】策略模式详解

策略模式

策略模式是一种行为设计模式,它定义了一系列算法,并将每一个算法封装起来,使它们可以互换。策略模式让算法的变化独立于使用算法的客户

组成

  1. 策略接口(Strategy):定义了一个所有具体策略的通用接口,各种不同的策略都遵循这个接口,它声明了一个上下文用于执行策略的方法。

    public interface Strategy {
    	public void algorithmInterface();
    }
    
  2. 具体策略(Concrete Strategy):实现策略接口的类,提供具体的策略实现

    public class ConcreteStrategy implements Strategy {
    	public void algorithmInterface() {
    		// 具体实现
    	}
    }
    
  3. 上下文(Context):持有一个策略类的引用,仅通过策略接口与该对象进行交流,最终向客户端提供具体的策略实例。

    	public class Context {
    		private Strategy strategy;
    		public void contextInterface() {
    			// 具体实现
    		}
    	}
    

其关系可由下面的UML类图表示:
策略模式示意图

适用场景

  1. 当你想使用对象中各种不同的算法变体,并希望能在运行时切换算法时,可使用策略模式。

    策略模式让你能够将对象关联至可以不同方式执行特定子任务的不同子对象, 从而以间接方式在运行时更改对象行为。

    在一个图像处理应用中,你可能有多种滤镜算法,如黑白滤镜、模糊滤镜和锐化滤镜等。使用策略模式,你可以将每种滤镜算法抽象为一个策略类,并在运行时选择要应用的滤镜算法。这样,用户可以轻松切换不同的滤镜效果,而应用程序的核心图像处理逻辑与具体的滤镜算法实现细节分离开来。

  2. 当你有许多仅在执行某些行为时略有不同的相似类时,可使用策略模式。

    策略模式让你能将不同行为抽取到一个独立类层次结构中,并将原始类组合成同一个,从而减少重复代码

  3. 如果算法在上下文的逻辑中不是特别重要,使用该模式能将类的业务逻辑与其算法实现细节隔离开来。

    策略模式让你能将各种算法的代码、内部数据和依赖关系与其他代码隔离开来。不同客户端可通过一个简单接口执行算法, 并能在运行时进行切换。

    在一个电商平台上,你可能需要实现多种优惠活动,如满减活动、折扣活动和赠品活动等。使用策略模式,你可以将每种优惠活动实现为一个策略类,并将优惠计算的逻辑委托给所选的优惠策略对象。这样,电商平台可以灵活地组合不同的优惠活动,而不需要修改核心的订单计算逻辑。

  4. 当类中使用了复杂条件运算以在同一算法的不同变体中切换时,可使用该模式。

    策略模式将所有继承自同样接口的算法抽取到独立类中,因此不再需要条件语句。原始对象并不实现所有算法的变体, 而是将执行工作委派给其中的一个独立算法对象。

    在一个在线支付系统中,你可能需要支持多种支付方式,如信用卡支付、支付宝支付和微信支付等。使用策略模式,你可以将每种支付方式实现为一个策略类,并将支付行为委托给所选的支付策略对象。这样,系统可以根据用户选择的支付方式动态地切换支付逻辑,而不需要在代码中编写复杂的条件语句。

实现方式

  1. 从上下文类中找出修改频率较高的算法 (也可能是用于在运行时选择某个算法变体的复杂条件运算)。

  2. 声明该算法所有变体的通用策略接口。

  3. 将算法逐一抽取到各自的类中, 它们都必须实现策略接口。

  4. 在上下文类中添加一个成员变量用于保存对于策略对象的引用。 然后提供设置器以修改该成员变量。 上下文仅可通过策略接口同策略对象进行交互, 如有需要还可定义一个接口来让策略访问其数据。

  5. 客户端必须将上下文类与相应策略进行关联, 使上下文可以预期的方式完成其主要工作。

软件设计原则:开闭原则
无需对上下文进行修改就能够引入新的策略。

评价

  • 优点

    • 使用组合来代替继承

      策略模式可以避免使用继承来实现不同的行为,而是通过组合和委托来实现。这样可以减少类之间的耦合度,并且更容易扩展和修改。

    • 可以将算法的实现和使用算法的代码隔离开

    • 避免繁琐的条件运算

      使用策略模式可以避免使用大量的 if-else 或 switch-case 语句来选择不同的算法或行为。这样可以使代码更加清晰、简洁,并且易于理解和维护。

    • 可以在运行时切换对象内的算法

      策略模式允许在运行时动态地选择和切换不同的算法或行为,从而使得程序更加灵活和可扩展。不同的策略类可以提供同种行为的不同实现,客户端可以根据需要选择合适的策略。

  • 缺点

    • 客户应知晓所有策略及它们之间的不同

      策略模式要求客户端知道所有可用的策略,并且需要自行选择合适的策略。这可能会增加客户端的复杂性和理解成本。

    • 策略和环境之间的通信开销

    策略模式将算法封装在不同的策略类中,这意味着在切换不同的策略时可能会涉及到策略和上下文之间的通信开销。如果频繁切换策略,可能会导致性能下降。

    • 太多策略的类

      当系统中存在大量的策略类时,可能会导致类的数量过多,增加系统的复杂度和维护成本。因此,在设计时需要权衡策略的数量和系统的复杂度。

示例

背景:假设我们正在开发一个游戏,其中角色具有不同的职业,不同职业的角色可以选择不同的攻击策略,比如使用剑攻击、魔法攻击或者弓箭攻击。

首先,我们选择攻击算法作为我们指定的通用策略接口,不同的攻击方式,如剑攻击、魔法攻击或者弓箭攻击。

然后,我们定义一个上下文类,这里我们指定Character基类,包含攻击策略,并为不同职业创建子类。

// 策略接口
interface AttackStrategy {
    void execute();
}

// 具体策略
class SwordAttack implements AttackStrategy {
    @Override
    public void execute() {
        System.out.println("Swings a mighty sword.");
    }
}

class MagicAttack implements AttackStrategy {
    @Override
    public void execute() {
        System.out.println("Casts a powerful spell.");
    }
}

class BowAttack implements AttackStrategy {
    @Override
    public void execute() {
        System.out.println("Shoots an arrow with precision.");
    }
}

abstract class Character {
    AttackStrategy attackStrategy;

    public Character(AttackStrategy attackStrategy) {
        this.attackStrategy = attackStrategy;
    }

    public void performAttack() {
        attackStrategy.execute();
    }

    public void setAttackStrategy(AttackStrategy attackStrategy) {
        this.attackStrategy = attackStrategy;
    }
}

// 不同职业的具体实现
class Knight extends Character {
    public Knight() {
        super(new SwordAttack());
    }
}

class Mage extends Character {
    public Mage() {
        super(new MagicAttack());
    }
}

class Archer extends Character {
    public Archer() {
        super(new BowAttack());
    }
}

public class Game {
    public static void main(String[] args) {
        Character knight = new Knight();
        knight.performAttack(); // Swings a mighty sword.

        Character mage = new Mage();
        mage.performAttack(); // Casts a powerful spell.

        Character archer = new Archer();
        archer.performAttack(); // Shoots an arrow with precision.

        // 动态改变攻击策略
        knight.setAttackStrategy(new MagicAttack());
        knight.performAttack(); // Casts a powerful spell.
    }
}

通过这种方式,我们保持了Character类的灵活性和扩展性,同时也利用了策略模式来动态地改变对象的行为。每个职业类继承自Character类,并通过构造方法指定了默认的攻击策略。这样设计的好处是,我们可以很容易地添加新的职业或攻击策略,而不必修改现有的类。此外,我们也可以在运行时改变任何角色的攻击策略,从而增加了游戏的灵活性和互动性。

下图清晰地展示了策略模式在此类游戏上的应用。

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值