In Action 实践

To illustrate this principle, let's consider an example session handing library. In fact, we will consider PHP's own SessionHandlerInterface. Here are the methods defined by this interface, which is included with PHP beginning in version 5.4:

为了说明该原则,我们来思考一个关于会话处理的类库。实际上我们将要考察PHP自己的SessionHandlerInterface。下面是该接口定义的方法,他们是从PHP 5.4版才开始有的:

interface SessionHandlerInterface {
    public function close();
    public function destroy($sessionId);
    public function gc($maxLifetime);
    public function open($savePath, $name);
    public function read($sesssionId);
    public function write($sessionId, $sessionData);
}

Now that you are familiar with the methods defined by this interface, consider an implementation using Memcached. Would a Memcached implementation of this interface define functionality for each of these methods? Not only do we not need to implement all of these methods, we don't need half of them!

现在我们知道接口里面都是什么方法了,我们打算用 Memcached 来实现它。Memcached 需要实现这个接口里的所有方法么?不,里面一半的方法对于 Memcached 来说都是不需要实现的!

Since Memcached will automatically expire the values it contains, we do not need to implement the gc method of the interface, nor do we need to implement the open or close methods of the interface. So, we are forced to define "stubs" for these methods in our implementation that are just empty methods. To correct this problem, let's start by defining a smaller, more focused interface for session garbage collection:

因为 Memcached 会自动清除存储的过期数据,我们不需要实现 gc 方法。我们也不需要实现 open 和 close 方法。所以我们被迫去写空方法来站着位子。为了解决在这个问题,我们来定义一个小巧的专门用来垃圾回收的接口:

interface GarbageCollectorInterface {
    public function gc($maxLifetime);
}

Now that we have a smaller interface, any consuming code can depend on this focused contract, which defines a very narrow set of functionality and does not create a dependency on the entire session handler.

现在我们有了一个小巧的接口,功能单一而专注。需要垃圾清理的只用依赖这个接口即可,而不必去依赖整个会话处理。

To further understand the principle, let's reinforce our knowledge with another example. Imagine we have a Contact Eloquent class that is defined like so:

为了更深入理解该原则,我们用另一个例子来强化理解。想象我们有一个名为Contact的Eloquent类,定义成这样:

class Contact extends Eloquent {
    public function getNameAttribute()
    {
        return $this->attributes['name'];
    }
    public function getEmailAttribute()
    {
        return $this->attributes['email'];
    }
}

Now, let's assume that our application also employs a PasswordReminder class that is responsible for sending password reminder e-mails to users of the application. Below is a possible definition of the PasswordReminder class:

现在我们再假设我们应用里还有一个叫 PasswordReminder 的类来负责给用户发送密码找回邮件。下面是 PasswordReminder 的定义方式的一种:

class PasswordReminder {
    public function remind(Contact $contact, $view)
    {
        // Send password reminder e-mail...
    }
}

As you probably noticed, our PassswordReminder is dependent upon the Contact class, which in turns means it is dependent on the Eloquent ORM. It is neither desirable or necessary to couple the password reminder system to a specific ORM implementation. By breaking the dependency, we can freely change our back-end storage mechanism or ORM without affecting the password reminder component of the application. Again, by breaking one of the SOLID principles, we have given a consuming class too much knowledge about the rest of the application.

你可能注意到了,PasswordReminder 依赖着 Contact 类,也就是依赖着 Eloquent ORM。 对于一个密码找回系统来说,依赖着一个特定的 ORM 实在是没必要,也是不可取的。切断对该 ORM 的依赖,我们就可以自由的改变我们后台存储机制或者说 ORM,同时不会影响到我们的密码找回组件。重申一遍,违背了“坚实”原则的任何一条,就意味着有个类它知道的太多了。

To break the dependency, let's create a RemindableInterface. In fact, such an interface is included with Laravel, and is implemented by the User model by default:

要切断这种依赖,我们来创建一个 RemindableInterface 接口。事实上 Laravel 已经有了这个接口,并且默认由 User 模型实现了该接口:

interface RemindableInterface {
    public function getReminderEmail();
}

Once the interface has been created, we can implement it on our model:

一旦接口定义好了,我们就可以在模型上实现它:

class Contact extends Eloquent implements RemindableInterface {
    public function getReminderEmail()
    {
        return $this->email;
    }
}

Finally, we can depend on this smaller, more focused interface in the PasswordReminder:

最终我们可以在 PasswordReminder 里面依赖这样一个小巧且专注的接口了:

class PasswordReminder {
    public function remind(RemindableInterface $remindable, $view)
    {
        // Send password reminder e-mail...
    }
}

By making this simple change, we have removed unnecessary dependencies from the password reminder component and made it flexible enough to use any class from any ORM, so long as that class implements the new RemindableInterface. This is exactly how the Laravel password reminder component remains database and ORM agnostic!

通过这小小的改动,我们已经移除了密码找回组件里不必要的依赖,并且使它足够灵活能使用任何实现了 RemindableInterface 的类或 ORM。这其实正是 Laravel 的密码找回组件如何保持与数据库 ORM 无关的秘诀!

Knowledge Is Power 知识就是力量

Again we have discovered the pitfalls of giving a class too much knowledge about application implementation details. By paying careful attention to how much knowledge we are giving a class, we can abide by all of the SOLID principles.

我们再次发现了一个使类知道太多东西的陷阱。通过小心留意是否让一个类知道了太多,我们就可以遵守所有的“坚实”原则。

results matching ""

    No results matching ""