Джефф, я думаю, у тебя правильная интуиция: это зависит.
Объектно-ориентированные иерархии классов с диспетчеризацией виртуальных методов хороши, когда у вас есть относительно фиксированный набор методов, которые необходимо реализовать, но многие потенциальные подклассы могут наследоваться от корня иерархии и реализовывать эти методы. В такой установке относительно легко добавить новые подклассы (просто реализовать все методы), но относительно сложно добавить новые методы (необходимо изменить все подклассы, чтобы убедиться, что они правильно реализуют новый метод).
Типы данных с функциональностью, основанной на сопоставлении с образцом, хороши, когда у вас есть относительно фиксированный набор классов, принадлежащих к типу данных, но многие потенциальные функции, которые работают с этим типом данных. В такой конфигурации относительно легко добавить новые функциональные возможности для типа данных (просто сопоставление с шаблоном во всех его классах), но относительно сложно добавить новые классы, которые являются частью типа данных (необходимо изменить все функции, которые соответствуют на тип данных, чтобы убедиться, что они правильно поддерживают новый класс).
Каноническим примером ОО-подхода является программирование GUI. Элементы GUI должны поддерживать очень небольшую функциональность (рисование себя на экране - минимум), но новые элементы GUI добавляются постоянно (кнопки, таблицы, диаграммы, ползунки и т. Д.). Каноническим примером подхода сопоставления с образцом является компилятор. Языки программирования обычно имеют относительно фиксированный синтаксис, поэтому элементы дерева синтаксиса изменяются редко (если вообще когда-либо), но новые операции над деревьями синтаксиса постоянно добавляются (более быстрая оптимизация, более тщательный анализ типов и т. Д.).
К счастью, Scala позволяет комбинировать оба подхода. Классы case могут быть сопоставлены с шаблоном и поддерживать диспетчеризацию виртуальных методов. Обычные классы поддерживают диспетчеризацию виртуальных методов и могут быть сопоставлены с шаблоном путем определения экстрактора в соответствующем сопутствующем объекте. Программист должен решить, когда каждый подход подходит, но я думаю, что оба полезны.