ジェフ、あなたの直感は正しいと思います。
仮想メソッド ディスパッチを使用したオブジェクト指向のクラス階層は、実装する必要があるメソッドのセットが比較的固定されている場合に適していますが、階層のルートから継承してそれらのメソッドを実装する潜在的なサブクラスが多数ある場合に適しています。このような設定では、新しいサブクラスを追加するのは比較的簡単ですが (すべてのメソッドを実装するだけです)、新しいメソッドを追加するのは比較的困難です (すべてのサブクラスを変更して、新しいメソッドが適切に実装されるようにする必要があります)。
パターン マッチングに基づく機能を備えたデータ型は、データ型に属するクラスのセットが比較的固定されている場合に適していますが、そのデータ型で動作する可能性のある関数が多数あります。このような設定では、データ型に新しい機能を追加するのは比較的簡単ですが (そのすべてのクラスでパターン マッチを行うだけです)、データ型の一部である新しいクラスを追加するのは比較的困難です (一致するすべての関数を変更する必要があります)。新しいクラスを適切にサポートしていることを確認するためのデータ型)。
OO アプローチの標準的な例は、GUI プログラミングです。GUI 要素がサポートする必要がある機能はほとんどありません (画面上に自分自身を描画することが最低限必要です) が、新しい GUI 要素 (ボタン、テーブル、チャート、スライダーなど) は常に追加されています。パターン マッチング アプローチの標準的な例はコンパイラです。プログラミング言語は通常、比較的固定された構文を持っているため、構文ツリーの要素が変更されることはほとんどありません (あったとしても) が、構文ツリーに対する新しい操作が常に追加されています (より高速な最適化、より徹底的な型分析など)。
幸いなことに、Scala では両方のアプローチを組み合わせることができます。ケース クラスは、パターン マッチングと仮想メソッド ディスパッチの両方をサポートできます。通常のクラスは仮想メソッドのディスパッチをサポートし、対応するコンパニオン オブジェクトでエクストラクタを定義することでパターン マッチングを行うことができます。それぞれのアプローチがいつ適切であるかを決定するのはプログラマ次第ですが、私はどちらも有用だと思います。