委託模式 (Delegation pattern)

2018/05/15 posted in  Design Pattern comments

在物件導向程式設計中,有個守則叫做:

「多用合成,少用繼承」(Composition over inheritance)

而Delegation就是一個用合成取代繼承的好方法。

"Delegate"

Definition

很多華語圈的開發者可能都對"Delegate"這個字有點陌生,我們先看看劍橋字典的解釋:

delegate /ˈdel.ɪ.ɡeɪt/ (v.)
to give a particular job, duty, right, etc. to someone else so that they do it for you
(把…)委派(給…),(把…)委託(給…);授權(給…)

在軟件工程的意思就是把一個class想做的功能委託給其他class幫忙做。

Delegation pattern

使用了這種「委託」式做法的程式設計,稱為「委託模式(Delegation pattern)」,最常見於C#、Objective-C和Swift。

The delegation design pattern allows an object to delegate one or more tasks to a helper object. [ref]

Part of speech

Delegate這個字還有幾種forms,也是常用,不要搞亂:

  • delegate (v.) - (把…)委派(給…)
  • delegate (n.) - 被委託者
  • delegator (n.) - 委託者
  • delegation (n.) - (一份)委託

Delegation vs Inheritance

"Delegation"和"Inheritance"經常可以做到相同的效果,例如以下的例子:

Inheritance

例如有個叫IMonitor的interface,有些顯示器是彩色的,有些是黑白的,於是你設計出兩種sub-classes:

public interface IMonitor {
    public void display();
}
public class ColorMonitor implements IMonitor {
    public void display() {
        System.out.println("Something colourful");
    }
}
public class BWMonitor implements IMonitor {
    public void display() {
        System.out.println("Something in B&W");
    }
}

ColorMonitor monitor = new ColorMonitor();
monitor.display();  // Something colourful

Delegation

這次我們只用一種顯示器,然後把display() delegate給其他class去做。

首先建立負責display()的delegates:

public interface DisplayDelegate {
    public void display();
}
public class ColorDisplayDelegate implements DisplayDelegate {
    public void display() {
        System.out.println("Something colourful");
    }
}
public class BWDisplayDelegate implements DisplayDelegate {
    public void display() {
        System.out.println("Something in B&W");
    }
}

然後在class裡面儲起一個負責display()的delegate:

public interface IMonitor {
    public void display();
}
public class ConcreteMonitor implements IMonitor {
    private DisplayDelegate displayDelegate;
    public ConcreteMonitor() {
        // Set up a delegate for displaying
        this.displayDelegate = new ColorDisplayDelegate();
    }
    public void display() {
        this.displayDelegate.display();
    }
}

ColorMonitor monitor = new ColorMonitor();
monitor.display();  // Something colourful

兩種做法最後效果相同。

那為甚麼要用Delegation?

Delegation最大的好處是Run-time flexibility

假設上述例子,雖然你有一部彩色顯示器 ColorMonitor monitor,但你可能會想把它當作黑白顯示器 BWMonitor用,在這情況下,Class的繼承是不能在Run-time改變的。

但對於Delegation卻是輕而易舉,只需要把monitor裡面的displayDelegate由彩色換成黑白的,就完全無痛改造了這個顯示器啦!

References

Appendix: 為甚麼要「多用合成,少用繼承」?