Хорошая статья. Написано очень дружелюбно для Java-разработчиков — это не частое свойство статей по scala.
Но вставлю пару уточнений:
1. Адаптер:
val log: Log = new Logger()
Обычно так не делают. Используют объект типа Logger (val log = new Logger() ) и прямо на нем вызывают методы из Log.
Если чуть изменить объявление LoggerToLogAdapter, то его экземпляр не будет создан вообще, даже для вызова его методов.
2. Объект-значение (Value object)
В добавок, case-классы являются валидными классами, а значит с ними можно работать как с обычными классами (например, наследоваться от них).
Не лучший пример. Наследование при использовании паттерна объект-значение крайне затруднительно независимо от языка. А scala обычно используют ADT где можно.
3. Null объекты (Null Object)
Автор предупредил, что не будет использовать сложные конструкции, но все-таки чтоб снять обвинение в многословности Option покажу как его в данном случае будет использовать любой, кто пишет на scala больше недели:
Про последний пример: мне кажется, если play возвращает Unit, лучше foreach, а не map использовать. Да и вообще во всех случаях, когда мы не планируем использовать возвращаемое значение.
Нет, не делает. foreach для Iterable просто перебирает итератор, как обычный джавовский цикл for. map же в обязательном порядке создаёт коллекцию — он принимает неявным аргументом CanBuildFrom, который используется для конструирования той же коллекции, как та, на которой map вызван.
Зря вы так про «любой, кто пишет на scala больше недели». Я вот скалашный for-синтаксис не люблю (то это map, то это foreach). Мне намного приятнее читать цепочку вызовов map, чем многострочный for. Опять же, то мне map нужен, то flatMap. И ещё мне не нравится что при чтении for-expression приходится постоянно читать то слева направо, то справа налево. Вот в вашем же примере:
SoundSource.getSound.foreach(sound => sound.play() ) // всё слево направо
А уж тем более проще
<source lang=«scala>
SoundSource.getSound.foreach(_.play() )
Если речь идёт о работе с коллекциями, то я с вами соглашусь – не всё так однозначно какой синтаксис более удобен. Но если речь о других типах, обладающих свойствами монада, то именно спец. синтаксис в виде «for» раскрывает всю красоту подхода. Для фьючеров, например, for-синтаксис намного более прозрачный, чем цепочки флэтмапов.
кстати, в примере на джабахе все же вызывается play, пусть и пустой, но вызов происходит. может я завтра туда запихну заглушку какую или логгер.
так что это сравнение выходит не особо идентичным, что может некоторых смутить в начале пути.
Зря написали про отсутствие минусов у кортежей и case-классов. Периодически спотыкаюсь на ограничение в 22 члена класса. Обычно при написании классов для ORM и других DTO.
Ещё не совсем удачно написан минус к декораторам, что в trait'ы нельзя передавать параметры конструктора. Фактически можно:
trait Buffering extends OutputStream {
def bufferSize: Int
abstract override def write(b: Byte) {
// ... buffer for bufferSize bytes
super.write(b)
}
}
new FileOutputStream("foo.txt") with Buffering { val bufferSize = 12 }
Я довольно много пишу на Scala. Сравнение прикольное, но сложилось впечатление, что автор намеренно описал только те паттерны, где у Scala есть либо красивый встроенный синтаксический сахар, либо возможности, которых нет в Java. А так +1.
Остальные паттерны в scala хуже реализовываться точно не будут. Так что зачем показывать те, что реализовываются так же или те, для описания которых требуется много кода.
На самом деле почти все можно на скале сделать лаконичнее при помощи DSL, но не всегда это оправдывает написание отдельного DSL.
Предпочитаю в качестве адаптера использовать неявную функцию преобразования, но данное решение наиболее подходит если оба трейта или класса имеют реализации методов
Классические паттерны проектирования на Scala