跳转至

Java 策略模式:灵活应对业务变化的利器

简介

在软件开发过程中,我们常常会遇到这样的情况:一个系统中的某个行为或算法,在不同的场景下需要有不同的实现方式。例如,电商系统中的促销活动,不同时期可能有不同的折扣策略;游戏中的角色行为,不同角色可能有不同的移动方式。策略模式就是为了解决这类问题而诞生的一种设计模式。它通过将算法的定义和使用分离,使得代码更加灵活、可维护和可扩展。

目录

  1. 策略模式基础概念
  2. 使用方法
  3. 常见实践
  4. 最佳实践
  5. 小结
  6. 参考资料

策略模式基础概念

策略模式定义了一系列算法,将每个算法都封装起来,并且使它们可以相互替换。在策略模式中,有三个主要角色: - 抽象策略(Strategy):定义了一个公共接口,所有具体策略类都必须实现这个接口。这个接口规定了算法的基本行为。 - 具体策略(Concrete Strategy):实现了抽象策略接口,提供了具体的算法实现。 - 上下文(Context):持有一个抽象策略类的引用,通过这个引用调用具体策略类的算法。

使用方法

代码示例

下面我们通过一个简单的示例来演示策略模式的使用。假设我们有一个电商系统,需要根据不同的促销活动计算商品的折扣价格。

  1. 定义抽象策略接口
public interface DiscountStrategy {
    double calculateDiscount(double originalPrice);
}
  1. 实现具体策略类
    • 无折扣策略
public class NoDiscountStrategy implements DiscountStrategy {
    @Override
    public double calculateDiscount(double originalPrice) {
        return originalPrice;
    }
}
- **打九折策略**
public class TenPercentDiscountStrategy implements DiscountStrategy {
    @Override
    public double calculateDiscount(double originalPrice) {
        return originalPrice * 0.9;
    }
}
- **满减策略**
public class CashbackDiscountStrategy implements DiscountStrategy {
    @Override
    public double calculateDiscount(double originalPrice) {
        if (originalPrice >= 100) {
            return originalPrice - 20;
        }
        return originalPrice;
    }
}
  1. 定义上下文类
public class ShoppingCart {
    private DiscountStrategy discountStrategy;

    public ShoppingCart(DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }

    public double calculateTotalPrice(double originalPrice) {
        return discountStrategy.calculateDiscount(originalPrice);
    }
}
  1. 测试代码
public class Main {
    public static void main(String[] args) {
        // 使用无折扣策略
        ShoppingCart cart1 = new ShoppingCart(new NoDiscountStrategy());
        double price1 = cart1.calculateTotalPrice(100);
        System.out.println("无折扣价格: " + price1);

        // 使用打九折策略
        ShoppingCart cart2 = new ShoppingCart(new TenPercentDiscountStrategy());
        double price2 = cart2.calculateTotalPrice(100);
        System.out.println("九折后价格: " + price2);

        // 使用满减策略
        ShoppingCart cart3 = new ShoppingCart(new CashbackDiscountStrategy());
        double price3 = cart3.calculateTotalPrice(100);
        System.out.println("满减后价格: " + price3);
    }
}

在这个示例中,DiscountStrategy 是抽象策略接口,NoDiscountStrategyTenPercentDiscountStrategyCashbackDiscountStrategy 是具体策略类,ShoppingCart 是上下文类。通过在 ShoppingCart 中传入不同的具体策略对象,我们可以根据不同的促销活动计算商品的折扣价格。

常见实践

替换条件语句

在没有使用策略模式之前,我们可能会使用大量的条件语句(如 if-elseswitch)来根据不同的情况选择不同的算法。例如:

public double calculateDiscount(double originalPrice, String discountType) {
    if ("noDiscount".equals(discountType)) {
        return originalPrice;
    } else if ("tenPercentDiscount".equals(discountType)) {
        return originalPrice * 0.9;
    } else if ("cashbackDiscount".equals(discountType)) {
        if (originalPrice >= 100) {
            return originalPrice - 20;
        }
        return originalPrice;
    }
    return originalPrice;
}

这种方式使得代码难以维护和扩展,当需要添加新的折扣策略时,需要修改这个方法,增加新的条件分支。而使用策略模式,我们只需要添加新的具体策略类,不需要修改上下文类的代码。

代码复用

策略模式可以将不同的算法封装在具体策略类中,这些具体策略类可以在不同的上下文中复用。例如,在一个游戏系统中,不同的角色可能有不同的移动方式(如步行、飞行、跳跃),我们可以将这些移动方式定义为具体策略类,然后在不同的角色类中复用这些策略。

最佳实践

依赖注入

在上下文类中,通过构造函数或 setter 方法注入具体策略对象,这样可以提高代码的灵活性和可测试性。例如:

public class ShoppingCart {
    private DiscountStrategy discountStrategy;

    // 通过构造函数注入
    public ShoppingCart(DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }

    // 通过 setter 方法注入
    public void setDiscountStrategy(DiscountStrategy discountStrategy) {
        this.discountStrategy = discountStrategy;
    }

    public double calculateTotalPrice(double originalPrice) {
        return discountStrategy.calculateDiscount(originalPrice);
    }
}

策略枚举

对于一些简单的策略,可以使用枚举来实现。枚举可以提供一种简洁的方式来定义和管理一组相关的策略。例如:

public enum DiscountEnum implements DiscountStrategy {
    NO_DISCOUNT {
        @Override
        public double calculateDiscount(double originalPrice) {
            return originalPrice;
        }
    },
    TEN_PERCENT_DISCOUNT {
        @Override
        public double calculateDiscount(double originalPrice) {
            return originalPrice * 0.9;
        }
    },
    CASHBACK_DISCOUNT {
        @Override
        public double calculateDiscount(double originalPrice) {
            if (originalPrice >= 100) {
                return originalPrice - 20;
            }
            return originalPrice;
        }
    }
}

使用时可以这样:

public class ShoppingCart {
    private DiscountStrategy discountStrategy;

    public ShoppingCart(DiscountEnum discountEnum) {
        this.discountStrategy = discountEnum;
    }

    public double calculateTotalPrice(double originalPrice) {
        return discountStrategy.calculateDiscount(originalPrice);
    }
}

策略工厂

当需要根据不同的条件动态创建具体策略对象时,可以使用策略工厂。策略工厂可以封装策略对象的创建逻辑,使得上下文类不需要关心具体策略对象的创建过程。例如:

public class DiscountStrategyFactory {
    public static DiscountStrategy getDiscountStrategy(String discountType) {
        if ("noDiscount".equals(discountType)) {
            return new NoDiscountStrategy();
        } else if ("tenPercentDiscount".equals(discountType)) {
            return new TenPercentDiscountStrategy();
        } else if ("cashbackDiscount".equals(discountType)) {
            return new CashbackDiscountStrategy();
        }
        return new NoDiscountStrategy();
    }
}

使用时可以这样:

public class ShoppingCart {
    private DiscountStrategy discountStrategy;

    public ShoppingCart(String discountType) {
        this.discountStrategy = DiscountStrategyFactory.getDiscountStrategy(discountType);
    }

    public double calculateTotalPrice(double originalPrice) {
        return discountStrategy.calculateDiscount(originalPrice);
    }
}

小结

策略模式通过将算法的定义和使用分离,使得代码更加灵活、可维护和可扩展。它可以有效替换复杂的条件语句,提高代码的复用性。在实际应用中,我们可以结合依赖注入、策略枚举和策略工厂等最佳实践,进一步优化代码结构,提高系统的性能和可维护性。通过掌握策略模式,我们能够更好地应对业务需求的变化,提高软件开发的效率和质量。

参考资料

  • 《设计模式 - 可复用的面向对象软件元素》(Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides 著)
  • 《Effective Java》(Joshua Bloch 著)
  • 维基百科 - 策略模式