1) Да, это способ выражения Scala алгебраических типов данных или различимых объединений , распространенных в функциональных языках программирования. Альтернативой здесь является наличие только одного класса с необязательными членами данных (либо с использованием Option
, который также имеет подкласс для пустого и подкласс для непустого, либо с использованием null
). Это заставляет все ваши методы проверять, есть ли у объекта данные или нет, делая их более сложными; использование подклассов имеет системную диспетчеризацию виртуального метода (что он собирается делать в любом случае), сделайте эту проверку за вас. Это также заставляет представленные данные быть непротиворечивыми ; Stack
либо имеет elem
и rest
(NonEmptyStack
), либо не имеет (EmptyStack
). У него не может быть одного, но нет другого (при условии, что никто намеренно не делает NonEmptyStack
с null
, что очень редко встречается в Scala).
Широко применима общая схема типов данных, являющаяся одним из нескольких случаев, когда к каждому случаю прилагаются разные данные. Наличие в одном из случаев данных - это просто тривиальный случай этого общего паттерна. Как программист Scala, использование этого общего шаблона станет вам знакомым, поэтому вполне естественно применять его и к простым случаям.
2) Вы заметите, что все методов в каждом дочернем классе немедленно возвращают значение без дальнейших вычислений (исключая случаи ошибок, которые немедленно генерируют исключение без дальнейших вычислений). Это делает их очень очевидными и простыми для понимания, если вы привыкли мыслить с точки зрения диспетчеризации виртуальных методов.
Кроме того, это делает их весьма эффективными; вычисление only , необходимое для определения того, что должен возвращать каждый метод, - это отправка виртуального метода, которую система все равно сделает для вас. Чтобы реализовать isEmpty
в родительском классе, вы должны добавить некоторую форму проверки и ветвления экземпляра; в любом случае это всего лишь ручная форма отправки виртуальных методов системы!
Более того, и я думаю, что самое важное, реализация дочернего класса более удобна в обслуживании. Допустим, вы добавили еще один специализированный вид непустого стека (возможно, у вас есть множество стеков с ровно одним элементом и вы не хотите тратить пространство на хранение дополнительной ссылки на пустой стек или что-то в этом роде). Если у вас есть ответвление в родительском элементе для возврата ответов, которые различны для разных подклассов, вы должны перейти и обновить каждый из них, чтобы учесть новый подкласс. И компилятор, вероятно, не заметит, если вы этого не сделаете. Если вы реализовали каждое специфичное для подкласса поведение в подклассе, то вы просто реализуете методы в новом подклассе.