Strategy Pattern

📢 This article was translated by gemini-2.5-flash

Strategy Pattern: Object Behavioral Pattern

Intent

Defines a family of algorithms, encapsulates each one, and makes them interchangeable. This pattern allows an algorithm to vary independently from the clients that use it.

Structure

策略模式

Here’s the breakdown:

  • Strategy: Defines a common interface for all supported algorithms. The Context uses this interface to call an algorithm defined by a ConcreteStrategy.

  • ConcreteStrategy: Implements a specific algorithm using the Strategy interface.

  • Context: Is configured with a ConcreteStrategy object; maintains a reference to a Strategy object; can define an interface to allow the Strategy to access its data.

Applicability

The Strategy pattern is useful when:

  • Many related classes differ only in their behavior. The “Strategy” provides a way to configure a class with one of multiple behaviors.

  • You need different variants of an algorithm. For example, algorithms that reflect different space/time tradeoffs. When these variants are implemented as a class hierarchy for an algorithm, the Strategy pattern can be used.

  • An algorithm uses data that clients shouldn’t know about. The Strategy pattern can hide complex, algorithm-specific data structures from clients.

  • A class defines many behaviors, and these appear as multiple conditional statements within its operations. Move related conditional branches into their respective Strategy classes to replace these statements.

Example 1

A large shopping mall wants to develop cashier software that supports various promotional activities over different periods, such as discounts or cashback (e.g., spend 300, get 100 back). The Strategy pattern is used to meet this requirement. The designed class diagram is shown below:

策略模式-例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import java.util.*;

enum TYPE { NORMAL, CASH_DISCOUNT, CASH_RETURN};

interface CashSuper {
    public double acceptCash(double money);
}

class CashNormal implements CashSuper { // Normal charging subclass
    public double acceptCash(double money) {
        return money;
    }
}
class CashDiscount implements CashSuper {
    private double moneyDiscount; // Discount rate
    public CashDiscount(double moneyDiscount) {
        this.moneyDiscount = moneyDiscount;
    }
    public double acceptCash(double money) {
        return money * moneyDiscount;
    }
}

class CashReturn implements CashSuper { // Full amount cashback
    private double moneyCondition;
    private double moneyReturn;
    public CashReturn(double moneyCondition, double moneyReturn) {
        this.moneyCondition = moneyCondition; // Threshold amount
        this.moneyReturn = moneyReturn; // Cashback amount
    }
    public double acceptCash(double money) {
        double result = money;
        if(money >= moneyCondition )
            result = money - Math.floor(money / moneyCondition) * moneyReturn;
        return result;
    }
}

class CashContext {
    private CashSuper cs;
    private TYPE t;
    public CashContext(TYPE t) {
        switch(t) {
            case NORMAL: // Normal charge
                cs = new CashNormal();
                break;
            case CASH_DISCOUNT: // 20% off
                cs = new CashDiscount(0.8);
                break;
            case CASH_RETURN: // Spend 300, get 100 back
                cs = new CashReturn(300, 100);
                break;
        }
    }
    public double GetResult(double money) {
        return cs.acceptCash(money);
    }
// main() method omitted for brevity
}

Example 2

Addition, Subtraction, Multiplication

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class StrategyPattern {
    public static void main(String[] args) {
        Strategy add = new AddStrategy();
        Strategy subtraction = new SubtractionStrategy();
        Strategy multiply = new MultiplyStrategy();

        OperationContext context = new OperationContext(add);
        context.Operation(1, 2);

        context = new OperationContext(subtraction);
        context.Operation(1, 2);

        context = new OperationContext(multiply);
        context.Operation(1, 2);
    }
}

class OperationContext{
    private Strategy strategy;

    public OperationContext(Strategy strategy){
        this.strategy = strategy;
    }

    public void Operation(int a, int b){
        strategy.operation(a, b);
    }

}

interface Strategy{
    public void operation(int a, int b);
}

class AddStrategy implements Strategy{
    @Override
    public void operation(int a, int b){
        System.out.println(a + b);
    }
}

class SubtractionStrategy implements Strategy{
    @Override
    public void operation(int a, int b){
        System.out.println(a - b);
    }
}

class MultiplyStrategy implements Strategy{
    @Override
    public void operation(int a, int b){
        System.out.println(a * b);
    }
}