ポリモーフィズムにはさまざまな種類がありますが、通常はランタイムポリモーフィズム/動的ディスパッチが対象です。
ランタイムポリモーフィズムの非常に高レベルの説明は、メソッド呼び出しは引数のランタイムタイプに応じて異なることを行うということです。オブジェクト自体がメソッド呼び出しを解決する責任があります。これにより、非常に大きな柔軟性が得られます。
この柔軟性を使用する最も一般的な方法の1つは依存性注入です。たとえば、異なる実装間で切り替えたり、テスト用にモックオブジェクトを注入したりできます。可能な選択肢が限られていることを事前に知っている場合、条件付きでそれらをハードコーディングすることができます。たとえば:
void foo() {
if (isTesting) {
... // do mock stuff
} else {
... // do normal stuff
}
}
これにより、コードを追跡しにくくなります。別の方法は、そのfoo操作のインターフェイスを導入し、そのインターフェイスの通常の実装とモック実装を記述し、実行時に目的の実装に「注入」することです。「依存性注入」は、「正しいオブジェクトを引数として渡す」ための複雑な用語です。
実世界の例として、私は現在、親切な機械学習の問題に取り組んでいます。予測モデルを必要とするアルゴリズムがあります。しかし、さまざまな機械学習アルゴリズムを試してみたいと思います。そこで、インターフェイスを定義しました。予測モデルには何が必要ですか?いくつかの入力サンプルが与えられると、予測とそのエラー:
interface Model {
def predict(sample) -> (prediction: float, std: float);
}
私のアルゴリズムは、モデルをトレーニングするファクトリー関数を使用します。
def my_algorithm(..., train_model: (observations) -> Model, ...) {
...
Model model = train_model(observations);
...
y, std = model.predict(x)
...
}
現在、モデルインターフェイスのさまざまな実装があり、それらを相互にベンチマークできます。これらの実装の1つは、実際には他の2つのモデルを取得し、それらを組み合わせてブーストモデルにします。このインターフェースのおかげで:
- 私のアルゴリズムは特定のモデルについて事前に知る必要はありません。
- モデルを簡単に交換できます。
- モデルの実装には非常に柔軟性があります。
ポリモーフィズムの典型的な使用例はGUIです。Java AWT / Swing /などのGUIフレームワークには、さまざまなコンポーネントがあります。コンポーネントインターフェイス/ベースクラスは、画面へのペイントやマウスクリックへの反応などのアクションを記述します。多くのコンポーネントは、サブコンポーネントを管理するコンテナです。このようなコンテナはどのようにそれ自体を描画しますか?
void paint(Graphics g) {
super.paint(g);
for (Component child : this.subComponents)
child.paint(g);
}
ここで、コンテナはサブコンポーネントの正確なタイプを事前に知る必要はありません- Component
インターフェースが準拠している限り、コンテナはポリモーフィックpaint()
メソッドを単に呼び出すことができます。これにより、AWTクラス階層を任意の新しいコンポーネントで拡張する自由が与えられます。
ポリモーフィズムを手法として適用することで解決できるソフトウェア開発全体に繰り返し発生する問題が多数あります。これらの繰り返し発生する問題とソリューションのペアはデザインパターンと呼ばれ、それらのいくつかは同じ名前の本に集められています。その本の用語では、私が注入した機械学習モデルは、「アルゴリズムのファミリを定義し、各アルゴリズムをカプセル化し、それらを交換可能にする」ために使用する戦略になります。コンポーネントにサブコンポーネントを含めることができるJava-AWTの例は、コンポジットの例です。
しかし、すべての設計でポリモーフィズムを使用する必要があるわけではありません(ユニットテストで依存性注入を有効にする以外に、これは本当に良いユースケースです)。それ以外の場合、ほとんどの問題は非常に静的です。その結果、クラスとメソッドはポリモーフィズムではなく、単に便利な名前空間として、またメソッド呼び出し構文として使用されます。たとえば、多くの開発者はaccount.getBalance()
、ほぼ同等の関数呼び出しよりもメソッド呼び出しを好みますAccount_getBalance(account)
。これは完全に素晴らしいアプローチです。多くの「メソッド」呼び出しはポリモーフィズムとは関係ありません。