說明
介面隔離原則的通俗描述如下
客戶端不應當被迫實現它不需要用到的介面
不好的示例
工人包括工作和睡覺兩種行為
class Worker {
public function work()
{
}
public function sleep()
{
}
}
機長則負責管理生產,需要管理生產要素
class Captain {
public function manage(Worker $worker)
{
$worker->work();
$worker->sleep();
}
}
但是生產要素不僅僅包括工人,機器也算。通常,我們會約定好「生產要素」
interface WorkerInterface {
public function work();
public function sleep();
}
再分別定義人和機器
class HumanWorker implements WorkerInterface {
public function work()
{
return 'human working'
}
public function sleep()
{
return 'human sleeping';
}
}
class AndroidWorker implements WorkerInterface {
public function work()
{
return 'Android working';
}
public function sleep()
{
return null;
}
}
現在,問題來了。機器沒有 sleep
這個行為,卻被迫去實現該介面。這明顯違反了介面隔離原則。
改進 1
首先,我們想到以將「工作」和「睡覺」兩個行為分別隔離出來
工作介面
interface WorkableInterface
{
public function work();
}
睡覺介面
interface SleepableInterface
{
public function sleep();
}
工人和機器根據自身情況去實現這些介面
class HumanWorker implements WorkableInterface, SleepableInterface
{
public function work()
{
return 'human working.';
}
public function sleep()
{
return 'human sleeping';
}
}
class AndroidWorker implements WorkableInterface
{
public function work()
{
return 'android working.';
}
public function beManaged()
{
$this->work();
}
}
機長
class Captain
{
public function manage($worker)
{
if($worker instanceof WorkableInterface){
$worker->work();
} else if($worker instanceof SleepableInterface){
$worker->sleep();
}
}
}
雖然我們分離了工作和睡覺兩個行為,避免了違反介面隔離原則。但是該例子仍然有問題,我們可以看出, Caption
類對 manage
是開放的,一旦新增新的生產要素,就必須去修改該程式碼,很明顯,違背了開放封閉原則。
最終改進
Captain
的變化的行為為 manage
,將其分離出來
interface WorkableInterface
{
public function work();
}
interface ManageableInterface
{
public function beManaged();
}
對應的生產要素分別實現相應的介面
class HumanWorker implements WorkableInterface, SleepableInterface, ManageableInterface
{
public function work()
{
return 'human working.';
}
public function sleep()
{
return 'human sleeping';
}
public function beManaged()
{
$this->work();
$this->sleep();
}
}
class AndroidWorker implements WorkableInterface, ManageableInterface
{
public function work()
{
return 'android working.';
}
public function beManaged()
{
$this->work();
}
}
保持 Captain
類的封閉性
class Captain
{
public function manage(ManageableInterface $worker)
{
$worker->beManaged();
}
}