声明:本文并非博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,当然也不是原汁原味的翻译,能保证90%的原汁性,另外因为是理解翻译,肯定会有错误的地方,欢迎指正。

欢迎转载,转载请注明出处,谢谢!

开放封闭原则

简介

在项目的整个生命周期中,绝大多数时间是在现有代码上的维护,而不是整天都在写新功能。你会意识到,这是一个让人头大的过程。任何对代码的修改,都会带来破坏原有设计,引入新bug的风险。理想状态下,我们应是像写新代码一样,去快速简单的修改老的逻辑。如果在设计中,能正确使用开放封闭原则,就能很好的规避这些问题。

开放封闭原则

SOLID设计原则中的开放封闭原则是指代码对扩展开放,对修改关闭。

实探

我们以上章中的OrderProcessor为基础,继续来探究开放封闭原则。对于process方法中的逻辑:

$recent = $this->orders->getRecentOrderCount($order->account);

if ($recent > 0)
{
    throw new Exception('Duplicate order likely.');
}

这段代码非常好读,使用依赖注入的方法也让我们易于测试。但是,如果针对验证的业务规则发生改编了怎么办?添加了新规则又怎么办?事实上,随着业务的发展,肯定会出现_很多_新的规则!process方法会很快的变得痈肿起来而难以维护。因为从开放封闭原则的角度考虑,他对修改是开放的,所以每当需求变更我们都要对代码进行改变。牢记,我们期望的是对_扩展_开放,而不是对修改开放。

代替掉在process中直接使用订单验证的方式,我们定义一个新接口OrderValidator

interface OrderValidatorInterface {

    public function validate(Order $order);

}

接下来,创建对重复订单验证的接口实现:

class RecentOrderValidator implements OrderValidatorInterface {

    public function __construct(OrderRepository $orders)
    {
        $this->orders = $orders;
    }

    public function validate(Order $order)
    {
        $recent = $this->orders->getRecentOrderCount($order->account);

        if ($recent > 0)
        {
            throw new Exception('Duplicate order likely.');
        }
    }

}

很好!现在一个小而可测的单独的业务规则封装类就完成了。我们来在创建一个检测用户是否被停用的接口实现:

class SuspendedAccountValidator implememts OrderValidatorInterface {

    public function validate(Order $order)
    {
        if ($order->account->isSuspended())
        {
            throw new Exception("Suspended accounts may not order.")
        }
    }

}

现在我们有了两个对接口OrderValidatorInterface的实现类,来看看怎么在OrderValidatorInterface中使用他们。我们只需在订单处理类实例化时注入验证类,这样就能在原有的订单处理代码基础上轻松的添加或删除验证规则。

class OrderProcessor {

    public function __construct(BillerInterface $biller,
    OrderRepository $orders,
    array $validators = array())
    {
        $this->biller = $biller;
        $this->orders = $orders;
        $this->validators = $validators;
    }

}

接下来,只需要在process方法中接入验证即可:

public function process(Order $order)
{
    foreach ($this->validators as $validator)
    {
        $validator->validate($order);
    }

    // Process valid order...
}

最后,我们须要将OrderProcessor绑定到应用程序IoC容器中:

App::bind('OrderProcessor', function()
{
    return new OrderProcessor(
        App::make('BillerInterface'),
        App::make('OrderRepository'),
        array(
            App::make('RecentOrderValidator'),
            App::make('SuspendedAccountValidator'),
        ),
    );
});

这些改变,只是在原有代码上产生最小的影响,我们现在就能不改变原来代码的基础上随便添加或者删除新的验证规则。

每个新的验证规则只是对OrderValidatorInterface的实现,并注入到容器中。不用对原来那种庞大臃肿的process方法进行单元测试,现在只需单独的对新验证规则测试即可。现在代码就是对扩展_开放_,对_修改_关闭。

玉有瑕疵

要注意依赖关系的实现细节。当依赖中的实现细节改变时,用户逻辑是不应该更随着改变的。当这种情况发生时,我们认为实现细节在以来关系中是“有漏洞的”。当抽象逻辑有漏洞,那么开闭原则也要被打破了。

在进一步处理之前,要知道开闭原则并非硬规定。不是代码的所有地方都得支持“热插拔”。例如,那种规模很小,只是简单的获取几行MySQL数据库数据的逻辑并不须要你严格按照那些设计原则去编写代码。不要只是为了用而在编码中硬赛上这些设计原则,这反而让你的系统过度设计,变得臃肿。很多设计原则是为了解决大而复杂系统时提出的常用解决方案。但是,别拿这些话作为你偷懒的借口。


laravel
101 声望55 粉丝