从验证到不变性保护机制

本文转载自【何以解耦】:codedecoupled.com/invariant.html

在应用程序中,无效状态是造成 Bug 出现的主要原因之一。特别是在像 PHP 这样的动态语言中,应用程序中尤其容易出现无效状态的对象。

举个例子,我们有创建了一个 EmailService 类,它可以发送邮件 EmailService::send()

class EmailService {
    public function send(string $email)
    {
        // 发送邮件逻辑
    }
}

以上代码有一个很大的隐患,它没有处理 $email 的无效的情况:$email 可能为 null,空或者无效的邮箱格式。

验证

处理类似问题的常见方案是采用验证,我们创建一个 EmailValidator 类:

class EmailValidator {
    public function isValid(string $email)
    {
        // 验证逻辑
    }
}

在使用 $email 之间,我们对其进行验证,比如在 EmailService 中,我们首先验证 $email,只有在它有效的情况下才发送邮件:

class EmailService {
    public function send(string $email)
    {
        if (!$this->emailValidator->isValid($email)){
            return;
        }

        // 发送邮件逻辑
    }
}

以上代码仍然有一个隐患:在代码层面,我们无法保证其他协同开发者会使用 EmailValidator 来验证 $email。不用提其他开发者,可能作者本人在过段时间后也可能疏忽大意。

不变性保护机制

与其在程序运行中对变量进行验证,为什么不在变量创建时保证其状态的有效性?这种模式便是不变性保护机制。

验证是第三方行为,而保护不变性是变量自身的机制。

使用不变性保护机制重构以上代码:

class Email
{
    private string $stringVal;

    public function __construct(string $stringVal)
    {
        if (!$this->isValid($stringVal)) {
            throw new \DomainException('Invalid email address');
        }
        $this->stringVal = $stringVal;
    }

    public function toString()
    {
        return $this->stringVal;
    }

}

class EmailService {
    public function send(Email $email)
    {
        // 发送邮件逻辑
    }
}

使用不变性保护机制不仅解决了上述已有问题,整体代码的重复使用性也得到了提高,同时 Email 类还提供了一个统一位置来管理邮箱地址,更不用提这种模式如何改善了单元测试的质量。

本文转载自【何以解耦】: codedecoupled.com/invariant.html ,如果你也对 TDD,DDD 以及简洁代码感兴趣,欢迎关注公众号【何以解耦】,一起探索软件开发之道。

本作品采用《CC 协议》,转载必须注明作者和本文链接
Know how, know why meanwhile.
xuding
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 2

重构之后的代码里,$this->isValid();这个方法在那实现的?Email类吗

2年前 评论
konakona 2年前
uniquespider (作者) 2年前
konakona 2年前

如果多个变量之间存在相互依赖,这种验证怎么做的?

2年前 评论
xuding (楼主) 2年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!