为了让 AI 用 5 个姿势生成图片,我差点写了 32 个 if-else

在这里插入图片描述

阅读大约需 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 #设计模式 #策略模式 #架构设计 #代码重构

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员义拉冠

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值