転送(Forwarding)と委譲(Delegation)の違いは何か?

「転送」と「委譲」の違い

オブジェクト指向プログラミングにおいて、「転送」と「委譲」は混同されがちである。

「2つのオブジェクトがあり、オブジェクトAの関数fooを呼んだ時、関数の内部で、オブジェクトBの関数fooが呼ばれる」という点では両者とも共通している。

違いは「オブジェクトBの関数fooが呼ばれた先で、オブジェクトAとオブジェクトBの、どちらのオブジェクトの関数barが呼ばれるか」という点である。

転送

class B {
    func foo() {
        this.bar()
    }
    func bar() {
        print("B bar")
    }
}

class A {
    func A(B b) {
        this.b = b
    }
    func foo() {
        this.b.foo()
    }
    func bar() {
        print("A bar")
    }
}

a = new A(new B())
a.foo()

// "B bar"

Aのfoo関数を呼ぶと、関数の内部でBのfoo関数が呼ばれ、その先でBのbar関数が呼ばれる。

委譲

class B {
    func foo() {
        this.delegate.bar()
    }
    func bar() {
        print("B bar")
    }
}

class A {
    func A(B b) {
        this.b = b
        this.b.delegate = this;
    }
    func foo() {
        this.b.foo()
    }
    func bar() {
        print("A bar")
    }
}

a = new A(new B())
a.foo()

// "A bar"

Aのコンストラクタで、Bのdelegateプロパティに、this(=A)を設定している。

Aのfoo関数を呼ぶと、関数の内部でBのfoo関数が呼ばれ、その先でA(=this.delegate)のbar関数が呼ばれる。

継承による委譲

委譲は、継承によっても実現できる。

class B {
    func foo() {
        this.bar()
    }
    func bar() {
        print("B bar")
    }
}

class A extends B {
    func foo() {
        super.foo()
    }
    func bar() {
        print("A bar")
    }
}

a = new A()
a.foo()

// "A bar"

Aのfoo関数を呼ぶと、関数の内部でスーパークラスBのfoo関数が呼ばれ、その先でthis(=A)のbar関数が呼ばれる。