HelloWorld 策略模式教程
2026年6月30日
•
作者:admin
策略模式(Strategy Pattern)是一种把可变行为封装为独立策略对象的设计方法。它通过定义统一接口,把算法从使用它的类中剥离出来,使得在运行时可以替换、组合或扩展这些行为。以 HelloWorld 为例,你可以把“如何输出”做成不同策略(纯文本、带时间戳、国际化前缀等),上下文只负责调用策略,从而减少条件分支、提高测试性与可维护性。

什么是策略模式(用最简单的话说)
想象你有一个会“说话”的小模块,它可以用不同方式说“Hello World”:普通朗读、打印带时间戳、或者替换成其他语言。策略模式就是把这些“说话方式”做成可插拔的零件。上下文(说话者)不关心具体怎么说,只调用“说话零件”的接口。这样新增或切换说话方式就像换电池一样简单。
核心要点(一句话)
- 分离变化:把会变的算法或行为封装为策略类。
- 运行时可替换:上下文在运行时持有策略并委托执行。
- 接口统一:所有策略都实现同一个接口,易于扩展与测试。
策略模式的组成(术语对应的角色)
| 角色 | 职责 |
| Strategy(策略接口) | 定义一组可互换的操作抽象,如 say() |
| ConcreteStrategy(具体策略) | 实现策略接口,提供具体算法或行为 |
| Context(上下文) | 持有一个 Strategy 引用,调用策略方法,不包含算法逻辑 |
HelloWorld 实战教程(逐步)
我们用费曼式分解法来讲,先把问题拆成最简单的块,然后逐步复用和组合。目标:把“打印 Hello World”变成可插拔的行为。
步骤 1:识别变化点
- 输出内容(Hello World、Bonjour le monde 等)
- 输出风格(纯文本、加前缀、带时间戳、写入文件等)
- 可能的输入参数(语言、格式化方式)
步骤 2:定义策略接口
接口通常非常小,只包含执行方法。例如:
interface HelloStrategy {
void say();
}
步骤 3:实现几个具体策略
举几个常用例子:
// 普通输出
class PlainHello implements HelloStrategy {
void say() { System.out.println("Hello World"); }
}
// 带时间戳
class TimestampHello implements HelloStrategy {
void say() { System.out.println(System.currentTimeMillis() + " Hello World"); }
}
// 国际化示例
class I18nHello implements HelloStrategy {
private String lang;
I18nHello(String lang) { this.lang = lang; }
void say() {
switch(lang) {
case "fr": System.out.println("Bonjour le monde"); break;
case "zh": System.out.println("你好,世界"); break;
default: System.out.println("Hello World");
}
}
}
步骤 4:实现上下文
上下文只持有一个策略引用,提供设置策略的方法与执行方法:
class HelloContext {
private HelloStrategy strategy;
HelloContext(HelloStrategy s) { this.strategy = s; }
void setStrategy(HelloStrategy s) { this.strategy = s; }
void execute() { strategy.say(); }
}
步骤 5:运行时切换
这样你可以按需切换:
HelloContext ctx = new HelloContext(new PlainHello());
ctx.execute(); // Hello World
ctx.setStrategy(new TimestampHello());
ctx.execute(); // 162... Hello World
ctx.setStrategy(new I18nHello("zh"));
ctx.execute(); // 你好,世界
多语言实现(快速示例)
下面给出 Python 和 JavaScript 的简单实现,对比差异:
Python 版本
from abc import ABC, abstractmethod
class HelloStrategy(ABC):
@abstractmethod
def say(self): pass
class PlainHello(HelloStrategy):
def say(self):
print("Hello World")
class TimestampHello(HelloStrategy):
def say(self):
import time
print(f"{int(time.time())} Hello World")
class HelloContext:
def __init__(self, strategy: HelloStrategy):
self._strategy = strategy
def set_strategy(self, s: HelloStrategy):
self._strategy = s
def execute(self):
self._strategy.say()
JavaScript(ES6)
class PlainHello {
say() { console.log("Hello World"); }
}
class TimestampHello {
say() { console.log(Date.now(), "Hello World"); }
}
class HelloContext {
constructor(strategy) { this.strategy = strategy; }
setStrategy(s) { this.strategy = s; }
execute() { this.strategy.say(); }
}
为什么用策略模式?好处与直观例子
- 减少条件分支:把 if/else 或 switch 拆成多个类,各自负责一种行为。
- 提高可测试性:每个策略都可以独立单元测试。
- 开闭原则:新增策略不需要修改上下文。
- 运行时灵活性:可以在程序运行期间替换策略,实现动态行为调整。
常见误区与何时不该用
- 不要为了避免少量条件而过度拆分:如果只有两三个简单分支,函数内 switch 可能更直观。
- 策略数量过多会增加类/对象管理复杂度,尤其在不支持依赖注入的小项目里。
- 如果策略间有大量共享状态,考虑组合(composition)或状态模式(State)代替。
与相近模式的对比
| 模式 | 区别 |
| Strategy | 把算法封装为独立类,客户端或上下文选择并使用 |
| State | 对象内部状态驱动行为切换,关注“状态”而不是“算法” |
| Template Method | 继承方式复用流程,子类重写步骤;策略是组合优先 |
| Command | 封装请求为对象,侧重调用时序与撤销;策略侧重替换算法 |
测试与实务建议
- 对每个策略写独立单元测试,验证边界输入与异常处理。
- 如果策略需要配置,尽量使用不可变参数或构造注入,避免全局状态。
- 在大型系统中配合依赖注入框架(如 Spring、Guice)实现策略注册与切换。
- 给策略命名要语义化:AvoidStrategy、CompactTimestampStrategy 之类笨拙名称会增加理解成本。
实际演练建议(小任务,练习掌握)
- 任务 1:实现五种 Hello 输出策略(Plain、Timestamp、UpperCase、FileWriter、i18n),并编写测试覆盖率到 90%。
- 任务 2:使用依赖注入自动选择策略:根据运行时配置加载不同策略实现。
- 任务 3:对现有包含大量 switch 的模块进行重构,用策略替代分支,评估代码行数和出错率变化。
实践中常见的扩展场景
策略模式并非只能替换输出行为,它对以下场景也非常有用:
- 排序策略(多种排序算法切换)
- 压缩/加密策略(不同算法按需替换)
- 支付方式、折扣规则、路由策略等业务规则引擎
小贴士(写给实战的你)
- 先写最简单的策略:从 Plain 实现开始,再抽象通用接口。
- 保持策略无副作用:尽量让策略方法只做一件事,便于重用与并发执行。
- 如果策略很小,可以用函数式风格代替类(很多语言支持函数作为一等公民)。
就像把一盒调色盘分成单色颜料一样,策略模式让你把“如何做”拆成可拼凑的模块。拿 HelloWorld 练练手,能迅速看到效果,然后慢慢把更复杂的业务逻辑也拆出来,你会发现代码变得更轻、更易测、也更好改——这是动手实践后才会体会到的小快乐。