如何使用SOLID/整洁代码/设计模式重构含switch分支的PHP函数?
重构方案
核心优化点
- 消除switch分支:将饮品类型与对应价格、提示文案抽为统一配置映射,符合开闭原则,后续新增饮品类目无需修改原有校验逻辑,仅需新增配置项即可
- 拆分校验逻辑:每个校验方法仅负责单一规则校验,符合单一职责原则,代码可读性和可维护性大幅提升
- 消除硬编码:所有阈值、提示文案均抽为可配置项,后续调整规则无需修改业务逻辑
重构后代码
// 先在类中定义统一配置常量,也可以根据需求放到配置文件中 protected const DRINK_PRICE_MAP = [ 'tea' => 0.4, 'coffee' => 0.5, 'chocolate' => 0.6, ]; protected const MIN_SUGARS = 0; protected const MAX_SUGARS = 2; protected function execute(InputInterface $input, OutputInterface $output): int { $this->setDrinkType($input); $drinkType = $this->drinkType; // 校验饮品类型合法性 if (!isset(self::DRINK_PRICE_MAP[$drinkType])) { $output->writeln('饮品类型仅支持茶、咖啡、巧克力'); return 0; } // 校验支付金额是否足够 $money = $input->getArgument('money'); $drinkPrice = self::DRINK_PRICE_MAP[$drinkType]; if ($money < $drinkPrice) { $output->writeln(sprintf('%s的售价为%.1f', $this->getDrinkDisplayName($drinkType), $drinkPrice)); return 0; } // 校验糖量合法性 if (!$this->hasCorrectSugars($input)) { $output->writeln(sprintf('糖量需在%d到%d之间', self::MIN_SUGARS, self::MAX_SUGARS)); return 0; } // 所有校验通过,输出订单信息 $this->printOrderInfo($input, $output); return 0; } protected function hasCorrectSugars($input): bool { $sugars = $input->getArgument('sugars'); return $sugars >= self::MIN_SUGARS && $sugars <= self::MAX_SUGARS; } // 把原checkSugars方法重命名为更符合功能的printOrderInfo,职责更清晰 protected function printOrderInfo($input, $output): void { $sugars = $input->getArgument('sugars'); $output->write('您已订购一杯' . $this->getDrinkDisplayName($this->drinkType)); $this->isExtraHot($input, $output); $output->write(',加' . $sugars . '份糖'); if ($sugars > 0) { $output->write('(附赠搅拌棒)'); } $output->writeln(''); } // 新增饮品名称映射方法,支持后续多语言或者显示名调整 protected function getDrinkDisplayName(string $drinkType): string { $displayNameMap = [ 'tea' => '茶', 'coffee' => '咖啡', 'chocolate' => '巧克力', ]; return $displayNameMap[$drinkType] ?? $drinkType; }
额外优化说明
- 原
allowedDrinkTypes属性可以直接移除,直接通过DRINK_PRICE_MAP的键判断合法饮品类型,避免两处维护同一份规则的不一致风险 - 如果后续业务复杂度提升,还可以把每种饮品做成独立的饮品类继承统一的饮品接口,把价格、显示名、自身校验规则都封装到对应类中,进一步解耦,更符合依赖倒置原则
- 所有返回码也可以抽为常量,比如
const RESPONSE_SUCCESS = 0,后续调整返回值无需全局修改
内容的提问来源于stack exchange,提问作者Rafael Pastor García
相关产品推荐
相关产品推荐

