深入理解Java策略模式

简介

在软件开发过程中,我们常常会遇到这样的情况:一个系统中存在多种相似的行为,这些行为在不同的场景下可能会有不同的实现方式。如果使用传统的条件判断语句(如if-elseswitch)来处理这些不同的行为,代码会变得冗长、难以维护,并且不符合开闭原则(对扩展开放,对修改关闭)。策略模式正是为了解决这类问题而诞生的一种设计模式。

策略模式定义了一系列的算法,将每一个算法封装起来,并使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户,从而提高了代码的可维护性和可扩展性。

目录

  1. 基础概念
  2. 使用方法
    • 定义策略接口
    • 实现具体策略类
    • 创建上下文类
    • 客户端使用
  3. 常见实践
    • 排序算法中的策略模式
    • 支付方式中的策略模式
  4. 最佳实践
    • 策略枚举
    • 依赖注入与策略模式结合
  5. 小结

基础概念

策略模式主要包含以下几个角色:

  • 策略接口(Strategy Interface):定义了一个公共接口,所有具体的策略类都必须实现这个接口。这个接口规定了具体策略类需要实现的方法。
  • 具体策略类(Concrete Strategy Classes):实现了策略接口,提供了具体的算法实现。每个具体策略类都代表了一种具体的行为方式。
  • 上下文类(Context Class):持有一个策略接口的引用,通过这个引用调用具体策略类的方法。上下文类将具体的策略实现细节封装起来,对外提供统一的调用接口。

使用方法

定义策略接口

首先,我们需要定义一个策略接口,该接口定义了所有具体策略类都需要实现的方法。例如,我们定义一个计算折扣的策略接口:

public interface DiscountStrategy {
    double calculateDiscount(double originalPrice);
}

实现具体策略类

接下来,我们实现具体的策略类,每个策略类都实现了策略接口中的方法,提供了不同的折扣计算方式。

public class PercentageDiscountStrategy implements DiscountStrategy {
    private double percentage;

    public PercentageDiscountStrategy(double percentage) {
        this.percentage = percentage;
    }

    @Override
    public double calculateDiscount(double originalPrice) {
        return originalPrice * (1 - percentage);
    }
}

public class FixedAmountDiscountStrategy implements DiscountStrategy {
    private double fixedAmount;

    public FixedAmountDiscountStrategy(double fixedAmount) {
        this.fixedAmount = fixedAmount;
    }

    @Override
    public double calculateDiscount(double originalPrice) {
        return originalPrice - fixedAmount;
    }
}

创建上下文类

上下文类持有一个策略接口的引用,并通过这个引用调用具体策略类的方法。

public class ShoppingCart {
    private DiscountStrategy discountStrategy;

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

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

客户端使用

在客户端代码中,我们可以根据不同的需求选择不同的策略类,并将其传递给上下文类。

public class Main {
    public static void main(String[] args) {
        // 使用百分比折扣策略
        DiscountStrategy percentageStrategy = new PercentageDiscountStrategy(0.2);
        ShoppingCart cart1 = new ShoppingCart(percentageStrategy);
        double totalPrice1 = cart1.calculateTotalPrice(100);
        System.out.println("使用百分比折扣后的总价: " + totalPrice1);

        // 使用固定金额折扣策略
        DiscountStrategy fixedAmountStrategy = new FixedAmountDiscountStrategy(20);
        ShoppingCart cart2 = new ShoppingCart(fixedAmountStrategy);
        double totalPrice2 = cart2.calculateTotalPrice(100);
        System.out.println("使用固定金额折扣后的总价: " + totalPrice2);
    }
}

常见实践

排序算法中的策略模式

在Java的集合框架中,Collections.sort()方法就使用了策略模式。Comparator接口就是策略接口,不同的Comparator实现类就是具体的策略类。

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class SortingExample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(5);
        numbers.add(2);
        numbers.add(8);
        numbers.add(1);

        // 升序排序策略
        Comparator<Integer> ascendingComparator = (a, b) -> a - b;
        Collections.sort(numbers, ascendingComparator);
        System.out.println("升序排序结果: " + numbers);

        // 降序排序策略
        Comparator<Integer> descendingComparator = (a, b) -> b - a;
        Collections.sort(numbers, descendingComparator);
        System.out.println("降序排序结果: " + numbers);
    }
}

支付方式中的策略模式

在一个电商系统中,可能有多种支付方式,如支付宝、微信、银行卡等。我们可以使用策略模式来实现不同支付方式的处理。

public interface PaymentStrategy {
    void pay(double amount);
}

public class AlipayStrategy implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("使用支付宝支付: " + amount + " 元");
    }
}

public class WeChatPayStrategy implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("使用微信支付: " + amount + " 元");
    }
}

public class CreditCardStrategy implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("使用银行卡支付: " + amount + " 元");
    }
}

public class Order {
    private PaymentStrategy paymentStrategy;

    public Order(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    public void processPayment(double amount) {
        paymentStrategy.pay(amount);
    }
}

public class PaymentExample {
    public static void main(String[] args) {
        Order order1 = new Order(new AlipayStrategy());
        order1.processPayment(100);

        Order order2 = new Order(new WeChatPayStrategy());
        order2.processPayment(200);

        Order order3 = new Order(new CreditCardStrategy());
        order3.processPayment(300);
    }
}

最佳实践

策略枚举

在某些情况下,策略的数量是有限且固定的,此时可以使用枚举来实现策略模式。枚举类可以实现策略接口,每个枚举常量就是一个具体的策略实现。

public interface DiscountStrategy {
    double calculateDiscount(double originalPrice);
}

public enum DiscountEnum implements DiscountStrategy {
    PERCENTAGE {
        @Override
        public double calculateDiscount(double originalPrice) {
            return originalPrice * 0.8;
        }
    },
    FIXED_AMOUNT {
        @Override
        public double calculateDiscount(double originalPrice) {
            return originalPrice - 20;
        }
    }
}

public class ShoppingCart {
    private DiscountStrategy discountStrategy;

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

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

public class Main {
    public static void main(String[] args) {
        ShoppingCart cart1 = new ShoppingCart(DiscountEnum.PERCENTAGE);
        double totalPrice1 = cart1.calculateTotalPrice(100);
        System.out.println("使用百分比折扣后的总价: " + totalPrice1);

        ShoppingCart cart2 = new ShoppingCart(DiscountEnum.FIXED_AMOUNT);
        double totalPrice2 = cart2.calculateTotalPrice(100);
        System.out.println("使用固定金额折扣后的总价: " + totalPrice2);
    }
}

依赖注入与策略模式结合

在企业级应用开发中,通常会使用依赖注入框架(如Spring)来管理对象的生命周期和依赖关系。将策略模式与依赖注入结合,可以更方便地切换和管理不同的策略。

首先,定义策略接口和具体策略类:

public interface DiscountStrategy {
    double calculateDiscount(double originalPrice);
}

@Component
public class PercentageDiscountStrategy implements DiscountStrategy {
    @Override
    public double calculateDiscount(double originalPrice) {
        return originalPrice * 0.8;
    }
}

@Component
public class FixedAmountDiscountStrategy implements DiscountStrategy {
    @Override
    public double calculateDiscount(double originalPrice) {
        return originalPrice - 20;
    }
}

然后,在上下文类中通过依赖注入获取策略对象:

@Service
public class ShoppingCart {
    private DiscountStrategy discountStrategy;

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

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

在客户端代码中,可以通过配置文件或注解来选择不同的策略:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        ShoppingCart cart1 = context.getBean(ShoppingCart.class);
        double totalPrice1 = cart1.calculateTotalPrice(100);
        System.out.println("使用百分比折扣后的总价: " + totalPrice1);

        // 可以通过修改配置来切换策略
        // 例如,将PercentageDiscountStrategy替换为FixedAmountDiscountStrategy
        ShoppingCart cart2 = context.getBean(ShoppingCart.class);
        double totalPrice2 = cart2.calculateTotalPrice(100);
        System.out.println("使用固定金额折扣后的总价: " + totalPrice2);
    }
}

小结

策略模式是一种强大的设计模式,它通过将算法封装在独立的策略类中,使得算法的变化不会影响到使用算法的客户代码。通过使用策略模式,我们可以提高代码的可维护性、可扩展性和可复用性。在实际开发中,我们可以根据具体的需求选择合适的实现方式,如普通的策略类实现、策略枚举或结合依赖注入框架来实现策略模式。希望通过本文的介绍,读者能够深入理解并熟练运用Java策略模式。