
在软件开发中,随着业务逻辑的增长,函数内部的条件判断会变得越来越复杂,尤其是当出现大型的 switch 语句或多层嵌套的 if-else 结构时。这不仅会降低代码的可读性,增加维护难度,还可能违反单一职责原则(srp)和开放/封闭原则(ocp)。本教程将以一个具体的饮品订单处理函数为例,展示如何运用清洁代码和设计模式的理念对其进行重构。
原始的 execute 函数负责处理饮品订单,包括验证饮品类型、检查金额是否足够以及处理糖的选项。其核心问题在于使用了 switch 语句来处理不同饮品的成本验证,导致代码重复且不易扩展。
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->setDrinkType($input);
if (in_array($this->drinkType, $this->allowedDrinkTypes)) {
// ... switch 语句处理饮品成本 ...
switch ($this->drinkType) {
case 'tea':
if ($money < 0.4) {
$output->writeln('The tea costs 0.4');
return 0;
}
break;
case 'coffee':
if ($money < 0.5) {
$output->writeln('The coffee costs 0.5');
return 0;
}
break;
case 'chocolate':
if ($money < 0.6) {
$output->writeln('The chocolate costs 0.6');
return 0;
}
break;
}
if ($this->hasCorrectSugars($input)) {
$this->checkSugars($input, $output);
return 0;
}
$output->writeln('The number of sugars should be between 0 and 2');
return 0;
}
$output->writeln('The drink type should be tea, coffee or chocolate');
return 0;
}此外,函数内部存在多层嵌套的 if 语句,使得逻辑流程难以追踪。hasCorrectSugars 和 checkSugars 两个函数虽然分离了部分逻辑,但它们的调用和错误处理仍与主流程紧密耦合。
我们的重构目标是提升代码的可读性、可维护性和可扩展性,主要通过以下几个方面实现:
switch 语句通常可以通过多态性或数据结构来消除。在这个案例中,饮品类型与成本的映射关系是固定的,非常适合使用关联数组(Map)来存储。
立即学习“PHP免费学习笔记(深入)”;
// 定义饮品成本映射,这可以是一个类成员变量或从配置中加载
protected array $drinkCosts = [
'tea' => 0.4,
'coffee' => 0.5,
'chocolate' => 0.6
];
// 在 execute 函数中
// ...
$money = $input->getArgument('money');
$drinkCost = $this->drinkCosts[$this->drinkType]; // 直接通过饮品类型获取成本
if ($money < $drinkCost) {
$output->writeln('The ' . $this->drinkType . ' costs ' . $drinkCost);
return 0;
}
// ...通过这种方式,我们不仅消除了 switch 语句,使得代码更加简洁,而且当需要添加新的饮品类型时,只需修改 $drinkCosts 数组,符合开放/封闭原则(OCP)——对扩展开放,对修改封闭。
卫语句是一种通过在函数开始处检查前置条件,并在条件不满足时立即返回或抛出异常来简化代码结构的技术。这可以有效减少 if-else 嵌套,使主逻辑更加清晰。
重构前:
if (in_array($this->drinkType, $this->allowedDrinkTypes)) {
// ... 大量逻辑 ...
} else {
$output->writeln('The drink type should be tea, coffee or chocolate');
return 0;
}重构后:
if (!in_array($this->drinkType, $this->allowedDrinkTypes)) {
$output->writeln('The drink type should be tea, coffee or chocolate');
return 0; // 不符合条件,立即返回
}
// 只有当饮品类型合法时,才继续执行后续逻辑同样,对于糖量检查和金额检查,也可以采用卫语句:
// 金额检查
if ($money < $drinkCost) {
$output->writeln('The ' . $this->drinkType . ' costs ' . $drinkCost);
return 0;
}
// 糖量检查
if (!$this->hasCorrectSugars($input)) {
$output->writeln('The number of sugars should be between 0 and 2');
return 0;
}通过卫语句,函数的主流程变得扁平化,每一层条件判断都处理了不符合预期的情况并提前退出,使得后续代码无需再考虑这些分支,逻辑路径更加清晰。
原始代码中 hasCorrectSugars 和 checkSugars 的命名和功能略有重叠,容易引起混淆。hasCorrectSugars 显然是用于验证糖量是否在有效范围内,而 checkSugars 似乎是用于输出订单信息。
hasCorrectSugars:这是一个纯粹的验证函数,其职责是判断糖量是否合法。
protected function hasCorrectSugars($input): bool
{
$sugars = $input->getArgument('sugars');
return ($sugars >= $this->minSugars && $sugars <= $this->maxSugars);
}checkSugars:这个函数实际上是在“处理”或“展示”糖量信息,包括输出订单详情。它的职责是根据糖量信息构建并输出用户订单的描述。
在重构后的 execute 函数中,先通过 hasCorrectSugars 进行验证,如果验证失败则提前返回错误信息;如果验证通过,再调用 checkSugars 进行后续的订单详情输出。这清晰地分离了验证逻辑和业务处理/输出逻辑。
结合上述重构策略,execute 函数的最终形态将变得更加简洁、可读:
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class OrderProcessor // 假设这是包含这些方法的类
{
protected string $drinkType;
protected array $allowedDrinkTypes = ['tea', 'coffee', 'chocolate'];
protected float $minSugars = 0;
protected float $maxSugars = 2;
// 饮品成本映射,可以作为类属性或通过构造函数注入
protected array $drinkCosts = [
'tea' => 0.4,
'coffee' => 0.5,
'chocolate' => 0.6
];
// 假设 setDrinkType 方法已存在并设置 $this->drinkType
protected function setDrinkType(InputInterface $input): void
{
$this->drinkType = $input->getArgument('drinkType'); // 示例:从输入获取饮品类型
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->setDrinkType($input);
// 1. 验证饮品类型 - 卫语句
if (!in_array($this->drinkType, $this->allowedDrinkTypes)) {
$output->writeln('The drink type should be tea, coffee or chocolate');
return 0; // 假设0代表失败或退出
}
// 2. 获取饮品成本并验证金额 - 消除switch,使用数据映射和卫语句
$money = $input->getArgument('money');
$drinkCost = $this->drinkCosts[$this->drinkType];
if ($money < $drinkCost) {
$output->writeln('The ' . $this->drinkType . ' costs ' . $drinkCost);
return 0;
}
// 3. 验证糖量 - 卫语句
if (!$this->hasCorrectSugars($input)) {
$output->writeln('The number of sugars should be between 0 and 2');
return 0;
}
// 4. 处理并输出糖量信息
$this->checkSugars($input, $output);
// 假设0代表成功
return 0;
}
protected function hasCorrectSugars(InputInterface $input): bool
{
$sugars = $input->getArgument('sugars');
return ($sugars >= $this->minSugars && $sugars <= $this->maxSugars);
}
protected function checkSugars(InputInterface $input, OutputInterface $output): void
{
$sugars = $input->getArgument('sugars');
$output->write('You have ordered a ' . $this->drinkType);
// 假设 isExtraHot 方法存在并被调用
// $this->isExtraHot($input, $output);
$output->write(' with ' . $sugars . ' sugars');
if ($sugars > 0) {
$output->write(' (stick included)');
}
$output->writeln('');
}
}通过本教程的重构实践,我们展示了如何将一个包含复杂 switch 语句和多层嵌套的函数,转化为一个更简洁、更易于理解和维护的结构。核心思想包括:将条件逻辑转化为数据驱动、利用卫语句简化流程控制、以及明确函数职责以提升模块化。这些方法不仅提升了代码质量,也为未来的功能扩展奠定了坚实的基础,是编写清洁、可维护代码的关键实践。
以上就是PHP函数重构实践:优化条件逻辑与提升可维护性的详细内容,更多请关注php中文网其它相关文章!
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号