Interface Segregation Principle 接口隔離原則

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.
  • 我們再次發現了一個使類知道太多東西的陷阱。通過小心留意是否讓一個類知道了太多,我們就可以遵守所有的“堅實”原則。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章