从验证到不变性保护机制

本文转载自【何以解耦】: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
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 2

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

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

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

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

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