かならずしも一般的な原則ではないと思いますが、自分はこんな風に考えることが多いです。
ちなみに「interface」は「機能」を表すと捉え、「(abstract or concrete) class」は「特定の実装」を表すものと捉えます。
###interface
オブジェクトを設計する際、備えるべき機能の定義は大抵interfaceとして考えます。特に
(I1) 特定のメソッドの引数に渡したいもの。または特定のメソッドの戻り値になるもの。
ちょっと言葉が足りないですね。より厳密に言うなら「複数の異なる具象クラスのインスタンスを扱うようなメソッドにおいて引数や戻り値の型になるようなもの」はabstract classではなくinterfaceとして考えるといった方がよいと思います。
引数を「interfaceではなくabstract classにする」のは(非常に大雑把に言うなら)そのメソッドが引数に渡されるインスタンスに対して必要以上の前提を置く(実装にかかわる詳細にアクセスしようとしているかも知れない)ように見えてあまりよいことに思えません。これは単純に「常にそうだ」と言えるようなものではなく場面によります。
なお、実引数を渡す際に
- 具象クラス(名前付きクラス)のインスタンス
- 具象クラス(無名クラス)のインスタンス
- ラムダ式
等々の方法がありますが、特に強調したいのは「型」を表す際にメソッドが一つしかないようなものはinterface(典型的にはFunctionalInterfaceアノテーション付きのinterface)として定義しておくとラムダ式が使え大変重宝します。関数型プログラミングという言葉を聞いたことがあるかも知れません。特にjava.util.Arrays#sort(T[] a, Comparator<? super T> c)
のようなメソッドを定義したい場合、interface(FunctionalInterface)とラムダ式は大変使い勝手がよいものです。
(I2)複数の機能を継承させたい(mix-inさせたい)場合
多用するわけではありません。しかし、たまにそういうことがしたくなる場合があります。簡単にいえば多重継承的なことがしたくなった時ということになります。こういう場合はインターフェースにするしかありません。抽象クラスにせよ具象クラスにせよ、あるクラスへ継承できるクラスは一つのみに制限されるからです。
###abstract class
よく考えてみると・・・これあまり使いません。インターフェースでは記述不可能な「共通的な実装」(典型的にはインスタンスフィールドによる実装)を備えさせたいときは使うことになりますが・・・。
抽象クラスは「ライブラリーを提供する場合」「ある程度大きな規模の応用プログラムにおいて、互いに重なりあう機能を備えたクラスがいくつも出てくるような場合」ならよく使われる気がします。しかしながら「機能に重なりがあるようなクラス群がそれほどないような応用プログラムを書く場合」では登場する機会が全然ないことも多いと思います。
余談:
ところでオブジェクト指向に備わったこうした機構について考えるのは「ある程度複数な機能を実装する」とか「既に作ってしまった機能に手を入れたい」というような設計をを繰り返しやりながら「あー設計がいけてない!」という経験をある程度つまないとなんでそういう考え方をするのかがなかなか分かりずらいと思います。
逆にいえばそうした「なんてイケテナイ設計なんだ!」と感じる経験をつむにつれ「こういう場合はこうして、ああいう場合はこうしたほうが・・・」という考えが自然にわいてくるような気がします。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
退会済みユーザー
2018/01/17 01:06 編集
2018/01/17 01:43