
阅读大约需 10 分钟
上回说到
小禾用适配器模式统一了 LLM 接口,用工厂模式管理创建逻辑,又写了个容错解析器专治 AI 的"薛定谔 JSON"。
三板斧下去,代码清爽多了。
小禾以为可以消停几天。
然后产品经理来了。
产品经理的连环 combo
“小禾,我们的图像生成功能太单一了,竞品有好多高级功能。”
小禾放下咖啡:“什么功能?”
"比如……"产品经理掏出手机,“你看这个,可以根据参考图保持人物一致。”
“嗯。”
“还有这个,可以参考一张图的风格。”
“嗯嗯。”
“还有这个,可以控制人物的姿势。”
“嗯嗯嗯。”
“还有这个……”
"停。"小禾举手投降,“你一共要几个功能?”
产品经理数了数:“5 个吧。而且要能任意组合。”
小禾算了算:5 个功能,任意组合,2⁵ = 32 种情况。
咖啡突然不香了。

if-else 金字塔
小禾硬着头皮写了第一版代码:
def generate_image(prompt, **options):
if options.get('keep_character'):
if options.get('keep_style'):
if options.get('control_pose'):
# 角色 + 风格 + 姿势
return generate_v1(...)
else:
# 角色 + 风格
return generate_v2(...)
elif options.get('control_pose'):
# 角色 + 姿势
return generate_v3(...)
else:
# 只有角色
return generate_v4(...)
elif options.get('keep_style'):
if options.get('control_pose'):
# 风格 + 姿势
return generate_v5(...)
else:
# 只有风格
return generate_v6(...)
elif options.get('control_pose'):
# 只有姿势
return generate_v7(...)
else:
# 纯文本
return generate_v8(...)
三个功能,8 个分支。
产品经理要 5 个功能,那就是 32 个分支。
小禾看着这座 if-else 金字塔,陷入了沉思。
不可能,绝对不可能!
新人的提问
实习生小张路过,探头看了一眼:“禾哥,你这代码好壮观啊。”
小禾苦笑:“壮观个屁,这是屎山。”
“那怎么办?”
小禾想起之前用的设计模式——适配器、工厂。
但这次不太一样。适配器是统一接口,工厂是创建对象。而现在的问题是:同一个功能,有多种实现方式,需要运行时选择。
小禾突然想起一个词:策略模式。
策略模式:把每种方式装进独立的盒子
核心思想很简单:
把每种生成方式封装成一个独立的"策略"类,运行时根据情况选择合适的策略。
打个比方:
你去旅游,可以坐飞机、高铁、自驾。
- 赶时间 → 选飞机
- 省钱 → 选高铁
- 带宠物 → 选自驾

你不会写成:
if 赶时间:
if 带宠物:
# 坐飞机还是自驾?
else:
坐飞机()
elif 省钱:
# ...
而是:
strategies = {
"飞机": FlyStrategy(),
"高铁": TrainStrategy(),
"自驾": DriveStrategy(),
}
strategy = select_best_strategy(我的情况)
strategy.execute()
代码里也一样。
定义策略接口
首先,定义一个"合同",所有策略都要遵守:
from abc import ABC, abstractmethod
class ImageGenerationStrategy(ABC):
"""图像生成策略的抽象基类"""
@abstractmethod
def generate(self, prompt: str, **params) -> str:
"""
生成图像
Args:
prompt: 文本描述
**params: 其他参数
Returns:
生成的图片路径
"""
pass
@property
@abstractmethod
def name(self) -> str:
"""策略名称,方便日志和调试"""
pass
这个基类说:“不管你是什么策略,对外都要提供 generate() 方法,输入输出格式统一。”
实现具体策略
策略 1:纯文本生成
最基础的,根据文字描述生成图片:
class Text2ImageStrategy(ImageGenerationStrategy):
"""纯文本生成:最基础的方式"""
@property
def name(self) -> str:
return "text2image"
def generate(self, prompt: str, **params) -> str:
logger.info(f"策略 [{self.name}] 开始生成")
# 调用底层图像生成接口
image = self._call_generation_api(
prompt=prompt,
width=params.get('width', 1024),
height=params.get('height', 1024),
)
# 保存并返回路径
return save_image(image)
策略 2:保持角色一致
让生成的人物和参考图保持一致(同一个人):
class CharacterConsistentStrategy(ImageGenerationStrategy):
"""角色一致性:生成的人物和参考图是同一个人"""
@property
def name(self) -> str:
return "character_consistent"
def generate(self, prompt: str, **params) -> str:
reference_image = params.get('character_ref')
if not reference_image:
raise ValueError("角色一致性策略需要提供参考图")
logger.info(f"策略 [{self.name}] 使用参考图: {reference_image}")
# 提取参考图的特征,生成时保持一致
image = self._call_generation_api(
prompt=prompt,
reference=reference_image,
mode="character",
)
return save_image(image)
策略 3:风格迁移
参考另一张图的风格(色调、笔触、氛围):
class StyleTransferStrategy(ImageGenerationStrategy):
"""风格迁移:参考另一张图的风格"""
@property
def name(self) -> str:
return "style_transfer"
def generate(self, prompt: str, **params) -> str:
style_image = params.get('style_ref')
if not style_image:
raise ValueError("风格迁移策略需要提供风格参考图")
logger.info(f"策略 [{self.name}] 参考风格: {style_image}")
image = self._call_generation_api(
prompt=prompt,
reference=style_image,
mode="style",
)
return save_image(image)
策略 4:姿势控制
让生成的人物保持指定的姿势:
class PoseControlStrategy(ImageGenerationStrategy):
"""姿势控制:让人物保持指定姿势"""
@property
def name(self) -> str:
return "pose_control"
def generate(self, prompt: str, **params) -> str:
pose_image = params.get('pose_ref')
if not pose_image:
raise ValueError("姿势控制策略需要提供姿势参考图")
logger.info(f"策略 [{self.name}] 参考姿势: {pose_image}")
image = self._call_generation_api(
prompt=prompt,
reference=pose_image,
mode="pose",
)
return save_image(image)
策略选择器:自动选合适的
有了 4 个策略,怎么知道用哪个?
写个选择器:
class StrategySelector:
"""根据参数自动选择策略"""
def __init__(self):
self._strategies = {
"text2image": Text2ImageStrategy(),
"character": CharacterConsistentStrategy(),
"style": StyleTransferStrategy(),
"pose": PoseControlStrategy(),
}
def select(self, **params) -> ImageGenerationStrategy:
"""根据参数选择合适的策略"""
# 有角色参考 → 角色一致性策略
if params.get('character_ref'):
return self._strategies["character"]
# 有风格参考 → 风格迁移策略
if params.get('style_ref'):
return self._strategies["style"]
# 有姿势参考 → 姿势控制策略
if params.get('pose_ref'):
return self._strategies["pose"]
# 默认 → 纯文本生成
return self._strategies["text2image"]
def get_by_name(self, name: str) -> ImageGenerationStrategy:
"""按名称获取策略(用于手动指定)"""
if name not in self._strategies:
available = list(self._strategies.keys())
raise ValueError(f"未知策略: {name},可用: {available}")
return self._strategies[name]
服务层:优雅调用
class ImageGenerationService:
"""图像生成服务"""
def __init__(self):
self.selector = StrategySelector()
def generate(self, prompt: str, **params) -> str:
"""生成图像"""
# 1. 选择策略
strategy = self.selector.select(**params)
logger.info(f"选中策略: {strategy.name}")
# 2. 执行生成
result = strategy.generate(prompt, **params)
logger.info(f"生成完成")
return result
调用方的代码变得超级简单:
service = ImageGenerationService()
# 纯文本生成
path = service.generate("一只橘猫在阳光下打盹")
# 保持角色一致
path = service.generate(
"一个女孩在咖啡馆看书",
character_ref="./reference/girl.jpg"
)
# 风格迁移
path = service.generate(
"城市夜景",
style_ref="./styles/cyberpunk.jpg"
)
# 姿势控制
path = service.generate(
"一个人在跳舞",
pose_ref="./poses/dance.jpg"
)
调用方完全不需要知道底层用了哪个策略。
传什么参数,就用什么策略。
那如果需要组合呢?
产品经理又来了:“小禾,用户想同时用角色一致和姿势控制,行不行?”
小禾想了想,有两条路。
路线 1:加组合策略
class CharacterWithPoseStrategy(ImageGenerationStrategy):
"""角色 + 姿势:组合策略"""
@property
def name(self) -> str:
return "character+pose"
def generate(self, prompt: str, **params) -> str:
character_ref = params['character_ref']
pose_ref = params['pose_ref']
# 同时应用两种约束
image = self._call_generation_api(
prompt=prompt,
character_reference=character_ref,
pose_reference=pose_ref,
)
return save_image(image)
然后更新选择器:
def select(self, **params) -> ImageGenerationStrategy:
# 组合情况优先判断
if params.get('character_ref') and params.get('pose_ref'):
return self._strategies["character+pose"]
# 单一情况
if params.get('character_ref'):
return self._strategies["character"]
# ...
路线 2:统一策略(组合太多时)
如果组合多到爆炸,可以用一个"万能策略":
class UnifiedStrategy(ImageGenerationStrategy):
"""统一策略:根据参数动态决定"""
@property
def name(self) -> str:
return "unified"
def generate(self, prompt: str, **params) -> str:
# 收集所有启用的功能
features = []
if params.get('character_ref'):
features.append(('character', params['character_ref']))
if params.get('style_ref'):
features.append(('style', params['style_ref']))
if params.get('pose_ref'):
features.append(('pose', params['pose_ref']))
logger.info(f"启用功能: {[f[0] for f in features]}")
# 调用底层接口,传入所有功能配置
image = self._call_generation_api(
prompt=prompt,
features=features,
)
return save_image(image)
这样不管多少种组合,都是一个策略类搞定。
重构成果
小禾花了一天重构完,对比了一下:
| 重构前 | 重构后 | |
|---|---|---|
| 代码行数 | 500+ 行(一个函数) | 每个策略 30-50 行 |
| 新增功能 | 改原函数,加 if-else | 新建策略类 |
| 测试 | 要覆盖所有分支组合 | 每个策略独立测试 |
| 可读性 | 8 层 if-else 金字塔 | 扁平结构,一目了然 |
产品经理后来又加了 2 个功能,小禾只新建了 2 个策略类,原有代码一行没动。
"舒服了。"小禾伸了个懒腰。
「策略模式」什么时候用?
小禾总结了一下:
适合用策略模式的情况:
- 同一任务有多种实现方式
- 方式之间可能组合
- 未来可能新增更多方式
- 每种方式的逻辑比较复杂
不需要用策略模式的情况:
- 只有 2-3 种情况,而且不会再加
- 每种情况的逻辑很简单(几行代码)
- 你只是想炫技(别)
小禾的感悟
if-else 不是不能用,
但当它开始"筑金字塔"的时候,
就该考虑策略模式了。
每种方式一个类,
选择归选择,执行归执行。
代码清爽了,
头发也保住了。
小禾关掉电脑,准备下班。
完结撒花
四个设计模式——适配器、工厂、容错解析、策略——就像四把钥匙,分别解决了:
- 接口不统一
- 对象创建混乱
- 数据格式错误
- 算法选择爆炸
四把钥匙,四类问题。

明天,还有新的门等着他开。
下一篇预告:前端状态管理,我悟了
用户刷新页面,编辑了 2 小时的内容全没了……
敬请期待。
#Python #设计模式 #策略模式 #架构设计 #代码重构

被折叠的 条评论
为什么被折叠?



