接下来,我们学习策略模式。这个模式定义了一系列的算法,并将每一个算法封装起来,使它们可以互相替换。策略模式让算法独立于使用它的客户而变化。
“定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。” (Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.)
想象一下你要去某个地方旅行,你有多种出行方式可选:
这些出行方式就是不同的“策略”。你可以根据你的需求(时间、预算、偏好)选择其中一种策略。策略模式就是将这些不同的策略(算法)封装起来,让你可以轻松地切换它们,而不需要改变“去旅行”这个行为本身(Context)。
策略模式的主要目的:
if/else
或 switch
来选择,策略模式可以将这些分支转换为对不同策略对象的调用,从而消除条件语句。支付方式:
排序算法:
压缩文件:
导航软件的路线规划:
策略模式通常包含以下角色:
Context (上下文):
Strategy (策略接口或抽象类):
ConcreteStrategy (具体策略):
优点:
if/else
或 switch
等条件判断,使代码更清晰。缺点:
让我们以一个简单的计算器为例,它可以执行不同的数学运算(加法、减法、乘法)作为不同的策略。
// strategy.go (Strategy interface and concrete strategies)
package strategy
// OperationStrategy 策略接口
type OperationStrategy interface {
DoOperation(num1, num2 int) int
GetName() string // For demonstration
}
// OperationStrategy.java (Strategy interface)
package com.example.strategy;
public interface OperationStrategy {
int doOperation(int num1, int num2);
String getName(); // For demonstration
}
// strategy.go (continued)
package strategy
// --- AddOperation --- (具体策略:加法)
type AddOperation struct{}
func (s *AddOperation) DoOperation(num1, num2 int) int {
return num1 + num2
}
func (s *AddOperation) GetName() string { return "Addition" }
// --- SubtractOperation --- (具体策略:减法)
type SubtractOperation struct{}
func (s *SubtractOperation) DoOperation(num1, num2 int) int {
return num1 - num2
}
func (s *SubtractOperation) GetName() string { return "Subtraction" }
// --- MultiplyOperation --- (具体策略:乘法)
type MultiplyOperation struct{}
func (s *MultiplyOperation) DoOperation(num1, num2 int) int {
return num1 * num2
}
func (s *MultiplyOperation) GetName() string { return "Multiplication" }
// AddOperation.java
package com.example.strategy;
public class AddOperation implements OperationStrategy {
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
@Override
public String getName() {
return "Addition";
}
}
// SubtractOperation.java
package com.example.strategy;
public class SubtractOperation implements OperationStrategy {
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
@Override
public String getName() {
return "Subtraction";
}
}
// MultiplyOperation.java
package com.example.strategy;
public class MultiplyOperation implements OperationStrategy {
@Override
public int doOperation(int num1, int num2) {
return num1 * num2;
}
@Override
public String getName() {
return "Multiplication";
}
}
// calculator.go (Context)
package context // Renamed package to avoid conflict
import (
"../strategy"
"fmt"
)
// Calculator 上下文
type Calculator struct {
strategy strategy.OperationStrategy
}
// NewCalculator 创建计算器,可以传入初始策略
func NewCalculator(initialStrategy strategy.OperationStrategy) *Calculator {
fmt.Printf("Calculator created with initial strategy: %s\n", initialStrategy.GetName())
return &Calculator{strategy: initialStrategy}
}
// SetStrategy 允许在运行时更改策略
func (c *Calculator) SetStrategy(s strategy.OperationStrategy) {
fmt.Printf("Calculator: Changing strategy to %s\n", s.GetName())
c.strategy = s
}
// ExecuteStrategy 执行当前策略
func (c *Calculator) ExecuteStrategy(num1, num2 int) int {
fmt.Printf("Calculator: Executing strategy %s with numbers %d and %d\n",
c.strategy.GetName(), num1, num2)
result := c.strategy.DoOperation(num1, num2)
fmt.Printf("Calculator: Result = %d\n", result)
return result
}
// Calculator.java (Context)
package com.example.context;
import com.example.strategy.OperationStrategy;
public class Calculator {
private OperationStrategy strategy;
// Constructor injection
public Calculator(OperationStrategy strategy) {
System.out.println("Calculator created with initial strategy: " + strategy.getName());
this.strategy = strategy;
}
// Setter injection - allows changing strategy at runtime
public void setStrategy(OperationStrategy strategy) {
System.out.println("Calculator: Changing strategy to " + strategy.getName());
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2) {
System.out.printf("Calculator: Executing strategy %s with numbers %d and %d%n",
this.strategy.getName(), num1, num2);
int result = strategy.doOperation(num1, num2);
System.out.printf("Calculator: Result = %d%n", result);
return result;
}
}
// main.go (示例用法)
/*
package main
import (
"./context"
"./strategy"
"fmt"
)
func main() {
add := &strategy.AddOperation{}
subtract := &strategy.SubtractOperation{}
multiply := &strategy.MultiplyOperation{}
// 使用加法策略创建计算器
calculator := context.NewCalculator(add)
calculator.ExecuteStrategy(10, 5) // Output: 15
fmt.Println("\n--- Changing strategy to Subtraction ---")
calculator.SetStrategy(subtract)
calculator.ExecuteStrategy(10, 5) // Output: 5
fmt.Println("\n--- Changing strategy to Multiplication ---")
calculator.SetStrategy(multiply)
calculator.ExecuteStrategy(10, 5) // Output: 50
// 也可以直接创建带特定策略的计算器实例
fmt.Println("\n--- New calculator with Multiplication strategy ---")
calc2 := context.NewCalculator(multiply)
calc2.ExecuteStrategy(7, 8) // Output: 56
}
*/
// Main.java (示例用法)
/*
package com.example;
import com.example.context.Calculator;
import com.example.strategy.AddOperation;
import com.example.strategy.MultiplyOperation;
import com.example.strategy.OperationStrategy;
import com.example.strategy.SubtractOperation;
public class Main {
public static void main(String[] args) {
OperationStrategy add = new AddOperation();
OperationStrategy subtract = new SubtractOperation();
OperationStrategy multiply = new MultiplyOperation();
// Create calculator with Add strategy
Calculator calculator = new Calculator(add);
calculator.executeStrategy(10, 5); // Output: 15
System.out.println("\n--- Changing strategy to Subtraction ---");
calculator.setStrategy(subtract);
calculator.executeStrategy(10, 5); // Output: 5
System.out.println("\n--- Changing strategy to Multiplication ---");
calculator.setStrategy(multiply);
calculator.executeStrategy(10, 5); // Output: 50
// Another calculator instance with Multiply strategy directly
System.out.println("\n--- New calculator with Multiplication strategy ---");
Calculator calc2 = new Calculator(multiply);
calc2.executeStrategy(7, 8); // Output: 56
}
}
*/
这两个模式在结构上非常相似,但意图不同。在状态模式的文档中我们已经对比过,这里简单回顾:
策略模式是一种非常实用的行为设计模式,它使得算法的选择和实现与使用算法的客户端代码分离。通过将不同的算法封装在独立的策略类中,我们可以轻松地添加、删除或修改算法,而不会影响到客户端代码或其他算法。这大大提高了系统的灵活性、可维护性和可扩展性,是应对需求变化、实现“开闭原则”的有力工具。