はじめに
みなさん、オブジェクト指向で開発してますか?
オブジェクト指向の開発でよく言われる「委譲を使おう」ですが、たまーに「委譲と移譲」間違えているケースがあります。
同音異義語でややこしいですが、何が違うのか明らかにしたいと思います。
まずは言葉の意味から
【委譲】
意味:上のものが下のものに対して権限などを委ねること。
英語:delegate。
【移譲】
意味:対等の相手に譲り渡すこと。
英語:trasfer。
サンプル説明
このサンプルでは、Somethingクラスがある責務を持っていて、その責務の一つであるmethod_aメソッドを委譲する例を示したいと思います。
サンプルはrubyです。
サンプル(元のコード)
class Something
def method_a
# 何らかの処理
end
end
class Main
def self.call
Something.new.method_a
end
end
Main.call
method_aメソッドを委譲する理由としては
- method_aのコードが肥大化したので、Somethingクラスから切り出したい
- あるいは、Somethingクラス自体が肥大化したのでスリムにしたい
などが考えられます。
委譲(正しい例)
class Something
def method_a
other = Other.new
other.method_a
end
end
class Other
def method_a
# 何らかの処理
end
end
class Main
def self.call
# SomethingがOtherに委ねている
Something.new.method_a
end
end
Main.call
SomethingクラスからOtherクラスに責務を委譲します。
委譲には上下関係があるので、上にあたるSomthingクラスが窓口の役割を持つ点が変わらないことに注意しましょう。
移譲(間違った例)
class Something
# 移譲したので、method_aがなくなっている
end
class Other
def method_a
# 何らかの処理
end
end
class Main
def self.call
# 移譲したので、Otherを直接呼ぶ
Other.new.method_a
end
end
Main.call
こちらは委譲ではなく、移譲してしまった例です。
SomethingクラスとOtherクラスは対等な関係であり、Mainクラスからは、Somethingクラスを通さずにOtherクラスが呼び出されています。
どうして移譲じゃダメなのか?
その前に「カプセル化」と「凝集度」について触れておきたいと思います。
「カプセル化」について
冒頭でこのサンプルの説明として、
Somethingクラスがある責務を持っていて、その責務の一つであるmethod_aメソッドを委譲するという説明をしました。
Somethingクラスがどんなプロパティ(内部変数)を持っているかはわかりませんが、それらプロパティの管理についても責任があります。
そしてプロパティを外部から自由に操作できるように公開するのではなく、そのクラスのメソッドの中でしか操作できないようにすることを「カプセル化」と言います。
この「カプセル化」をくだけた感じで説明すると
- 「プロパティについて、僕(Somethingクラス)に何でも聞いてください」
- 「値を変更するにしても、何するにしても、僕(Somethingクラス)に命じてくれるのなら、責任を持って管理します」
- 「僕(Somethingクラス)が管理してるので、外から無断で変更しないでください」
といった感じでしょうか。
「凝集度」について
「凝集」とは何でしょうか?
「散らばったりしていたものが、一つに集まること」という意味があります。
これを踏まえて「凝集度」をウィキペディアで見てみましょう。
凝集度は、あるコードがどれだけそのクラスの責任分担に集中しているかを示す尺度である。オブジェクト指向プログラミングでは、クラスの凝集度を高めるようにそのクラスの責任範囲を設定することが有益とされている。
凝集度が高いと、コードの読みやすさと再利用の容易さが増すとされています。
注意しなければならないのは、なんでもかんでも一つのクラスに集めれば良いということではないことです。クラスは「単一責任の原則」に則るべきであり、複数の責任を負うべきではありません。凝集度を高めるためには関係のある処理だけを集めるべきです。
そうやって単一責任を処理するためのメソッドがクラスに集まっている状態を「通信的凝集」が高いと言います。
あらためて、どうして移譲じゃダメなのか?
移譲すると
- 凝集度が下がる
- カプセル化がこわれることがある
からです。
凝集度が下がるとはどういうことでしょうか?
「散らばったりしていたものを一つに集めること」が凝集度を高めることであり
移譲するということは、集めることに反して、別の場所に移すことだからです。
カプセル化がこわれることがある?
プロパティ(インスタンス変数と読み替えても良いかもしれません)にアクセスできるのは
そのクラスのメソッドの中だけです。accessorにしたりgetter、setterを用意すると、それはカプセル化ではありません。
すべてのメソッドの移譲がカプセル化をこわすとは思いませんが、プロパティへのアクセスが必要なメソッドを移譲するには、カプセル化したままではできないことがあることに留意すべきです。
委譲だと良いの?
- 凝集度は下がらない
- カプセル化を維持できる
委譲の場合は、元のクラスに窓口としてのメソッドを残しますので、凝集度は下がっていないと言えます。
諸説ありますが、少なくともそのクラスに責務が集まっていることで、コードの理解やメンテナンス性は低下しないでしょう。
カプセル化を維持できる。
元のクラスにメソッドがあるので、元クラスが持つプロパティ(インスタンス変数)に自由にアクセスすることができます。必要であれば委譲先のクラスに対して引数としてプロパティを渡すことができます。
(この場合の注意点として、参照型を引数に渡して委譲先のクラスで書き換えられてしまうことによるカプセル化の破壊が考えられますが、ここではその対応には触れません)
最後に
私自身、オブジェクト指向の委譲を文字で書く時、委譲と移譲の漢字を間違うことがありましたが、もうこれで間違うことはないでしょう。