策略模式详解

本文深入探讨策略模式的概念,通过游戏角色和电商折扣系统实例,展示如何通过封装算法族实现灵活的算法选择与替换,提高代码的扩展性和维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

策略模式(Strategy Pattern)

定义

策略模式定义了算法族,分别封装起来,让它们之间可以相互替换,此模式使算法的变化独立于使用算法的客户。——《Head First设计模式》

模式总结过程
  1. 公司新开发一款打怪等级游戏,三个角色:猎人、剑士、枪手,所有角色都能移动、攻击和外观,但是有两个不同,1攻击的方式不同,猎人使用弓箭,剑士使用利剑,枪手使用手枪,2外观不同
// 抽象角色类,都能移动,攻击抽象,让子类实现
public abstract class AbstractRole {

    public void move() {
        System.out.println("移动");
    }

    public abstract void attack();
    
    public abstract void display();
}

// 猎人
public class HunterRole extends AbstractRole {
    @Override
    public void attack() {
        System.out.println("使用弓箭射击怪物");
    }
    
    @Override
    public void display() {
        System.out.println("我是一个猎人");
    }
}

// 剑士
public class SwordmanRole extends AbstractRole {
    @Override
    public void attack() {
        System.out.println("使用利剑攻击怪物");
    }
    
    @Override
    public void display() {
        System.out.println("我是一个剑士");
    }
}

// 枪手
public class GunnerRole extends AbstractRole {
    @Override
    public void attack() {
        System.out.println("使用手枪射击怪物");
    }
    
    @Override
    public void display() {
        System.out.println("我是一个枪手");
    }
}
  1. 版本升级,公司要求每个角色有默认的武器,但可以切换武器,开发的想法是,给角色一把武器,再给一个切换武器的方法
// 抽象角色
public abstract class AbstractRole {

    // 武器
    protected Weapon weapon;

    public void move() {
        System.out.println("移动");
    }

    // 玩家随意切换武器
    public void switchWeapon(Weapon weapon) {
        this.weapon = weapon;
    }

    // 调用武器进行攻击
    public void attack() {
        weapon.attack();
    }

    public abstract void display();
}

// 猎人
public class HunterRole extends AbstractRole {

    // 注入默认的武器
    public HunterRole() {
        this.weapon = new BowAndArrowWeapon();
    }

    @Override
    public void display() {
        System.out.println("我是一个猎人");
    }
}

// 剑士
public class SwordmanRole extends AbstractRole {

    // 注入默认的武器
    public SwordmanRole() {
        this.weapon = new BladeWeapon();
    }

    @Override
    public void display() {
        System.out.println("我是一个剑士");
    }
}

// 枪手
public class GunnerRole extends AbstractRole {

    // 注入默认的武器
    public GunnerRole() {
        this.weapon = new HandgunWeapon();
    }

    @Override
    public void display() {
        System.out.println("我是一个枪手");
    }
}

// 武器接口
public interface Weapon {
    void attack();
}

// 弓箭
public class BowAndArrowWeapon implements Weapon {
    @Override
    public void attack() {
        System.out.println("弓箭射击");
    }
}

// 利剑
public class BladeWeapon implements Weapon {
    @Override
    public void attack() {
        System.out.println("利剑攻击");
    }
}

// 手枪
public class HandgunWeapon implements Weapon {
    @Override
    public void attack() {
        System.out.println("手枪射击");
    }
}

// 客户端
public class Client {

    public static void main(String[] args) {
        // 猎人-默认武器-弓箭
        AbstractRole role = new GunnerRole();
        // 攻击
        role.attack();
        // 切换武器-利剑
        role.switchWeapon(new BladeWeapon());
        // 攻击
        role.attack();
    }
}
/*
手枪射击
利剑攻击
*/
  1. 后续再增加角色,则只需要多一个角色类,并覆盖角色的外观方法即可
  2. 如果增加武器,也只需要增加一个武器类的实现
设计原则
  1. 依赖倒转原则(针对接口编)
  2. 合成/聚合复用原则(多用组合,少用继承)
模式的重心

策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性

本例中,所有武器构成算法族,策略算法是相同行为的不同实现,可自由切换

优点
  1. 符合开闭原则
  2. 维护性高、扩展性高
  3. 代码逻辑清晰明了
缺点
  1. 客户端需要知道所有的算法(本例中的武器)
  2. 算法增多时,对象的数目就会很可观
更多例子

备注:引用网上比较好的例子(https://www.cnblogs.com/java-my-life/archive/2012/05/10/2491891.html

假设现在要设计一个贩卖各类书籍的电子商务网站的购物车系统。一个最简单的情况就是把所有货品的单价乘上数量,但是实际情况肯定比这要复杂。比如,本网站可能对所有的高级会员提供每本20%的促销折扣;对中级会员提供每本10%的促销折扣;对初级会员没有折扣。

根据描述,折扣是根据以下的几个算法中的一个进行的:

算法一:对初级会员没有折扣。

算法二:对中级会员提供10%的促销折扣。

算法三:对高级会员提供20%的促销折扣。

// 抽象折扣类
public interface MemberStrategy {
    /**
     * 计算图书的价格
     * @param booksPrice    图书的原价
     * @return    计算出打折后的价格
     */
    public double calcPrice(double booksPrice);
}

// 初级会员折扣类
public class PrimaryMemberStrategy implements MemberStrategy {

    @Override
    public double calcPrice(double booksPrice) {
        
        System.out.println("对于初级会员的没有折扣");
        return booksPrice;
    }

}

// 中级会员折扣类
public class IntermediateMemberStrategy implements MemberStrategy {

    @Override
    public double calcPrice(double booksPrice) {

        System.out.println("对于中级会员的折扣为10%");
        return booksPrice * 0.9;
    }

}

// 高级会员折扣类
public class AdvancedMemberStrategy implements MemberStrategy {

    @Override
    public double calcPrice(double booksPrice) {
        
        System.out.println("对于高级会员的折扣为20%");
        return booksPrice * 0.8;
    }
}

// 价格类
public class Price {
    //持有一个具体的策略对象
    private MemberStrategy strategy;
    /**
     * 构造函数,传入一个具体的策略对象
     * @param strategy    具体的策略对象
     */
    public Price(MemberStrategy strategy){
        this.strategy = strategy;
    }
    
    /**
     * 计算图书的价格
     * @param booksPrice    图书的原价
     * @return    计算出打折后的价格
     */
    public double quote(double booksPrice){
        return this.strategy.calcPrice(booksPrice);
    }
}

// 客户端
public class Client {

    public static void main(String[] args) {
        //选择并创建需要使用的策略对象
        MemberStrategy strategy = new AdvancedMemberStrategy();
        //创建环境
        Price price = new Price(strategy);
        //计算价格
        double quote = price.quote(300);
        System.out.println("图书的最终价格为:" + quote);
    }

}

从上面的示例可以看出,策略模式仅仅封装算法,提供新的算法插入到已有系统中,以及老算法从系统中“退休”的方法,策略模式并不决定在何时使用何种算法。在什么情况下使用什么算法是由客户端决定的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值