Interface Segregation Principle 接口隔离原则
Introduction 介绍
The Interface Segregation principle states that no implementation of an interface should be forced to depend on methods it does not use. Have you ever had to implement methods of an interface that you did not need? If so, you probably created blank methods in your implementation. This is an example of being forced to use an interface that breaks the Interface Segregation principle.
接口隔离原则规定在实现接口的时候,不能强迫去实现没有用处的方法。你是否曾被迫去实现一些接口里你用不到的方法?如果答案是肯定的,那你可能创建了一个空方法放在那里。被迫去实现用不到的函数,这就是一个违背了接口隔离原则的例子。
In practical terms, this principle demands that interfaces be granular and focused. Sound familiar? Remember, all five SOLID principles are related, such that by breaking one you often must break the others. When breaking the Interface Segregation principle, the Single Responsibility principle must also broken.
在实际操作中,该原则要求接口必须粒度很细,且专注于一个领域。听起来很耳熟?记住,所有五个“坚实”原则都是相关的,也就是说当打破一个原则时,你通常肯定打破了其他的原则。在这里当你违背了接口隔离原则后,肯定也违背了单一职责原则。
Instead of having a "fat" interface containing methods not needed by all implementations, it is preferable to have several smaller interfaces that may be implemented individually as needed. By breaking fat interfaces into smaller, more focused contracts, consuming code can depend on the smaller interface, without creating dependencies on parts of the application it does not use.
“臃肿”的接口,有着很多不是所有的实现类都需要的方法。与其写这样的接口,不如将其拆分成多个小巧的接口,里面的方法都是各自领域所需要的。这样将臃肿接口拆成小巧、功能集中的接口后,我们就可以使用小接口来编码,而不必为我们不需要的功能买单。
Interface Segregation Principle 接口隔离原则
This principle states that no implementation of an interface should be forced to depend on methods it does not use.
该原则规定,一个接口的一个实现类,不应该去实现那些自己用不到的方法。如果需要,那就是接口设计有问题,违背了接口隔离原则。
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 版才开始有的:
<!-- lang:php -->
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
方法。所以我们被迫去写空方法来站着位子。为了解决在这个问题,我们来定义一个小巧的专门用来垃圾回收的接口:
<!-- lang:php -->
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类,定义成这样:
<!-- lang:php -->
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
的定义方式的一种:
<!-- lang:php -->
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
模型实现了该接口:
<!-- lang:php -->
interface RemindableInterface {
public function getReminderEmail();
}
Once the interface has been created, we can implement it on our model:
一旦接口定义好了,我们就可以在模型上实现它:
<!-- lang:php -->
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
里面依赖这样一个小巧且专注的接口了:
<!-- lang:php -->
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.
我们再次发现了一个使类知道太多东西的陷阱。通过小心留意是否让一个类知道了太多,我们就可以遵守所有的“坚实”原则。