Pull to refresh

Comments 29

Хорошая статья. Написано очень дружелюбно для 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 больше недели:

for (sound <- SoundSource.getSound) {
  sound.play()
}

// аналогично
SoundSource.getSound.map{ _.play }
Про последний пример: мне кажется, если play возвращает Unit, лучше foreach, а не map использовать. Да и вообще во всех случаях, когда мы не планируем использовать возвращаемое значение.
Тогда я там и скобки «забыл».

Большинство методов — чистые, а, следовательно, возвращают значение. Так что я просто хотел показать наиболее частый случай.
Где вы скобки забыли? По-моему, все нужные скобки на месте :-) или я ошибаюсь?
Функции с сайдэффектами принято вызывать со скобками. _.play()
// аналогично
SoundSource.getSound.map{ _.play }

Вы слегка ошиблись. Аналогично будет
SoundSource.getSound.foreach(_.play)

Нам же не нужно здесь коллекцию Unitов строить)

Всегда буду обновлять комментарии, всегда буду обновлять комментарии...
Если не ошибаюсь, то foreach в кишках эту же коллекцию и делает.
«for» без «yield» сделает foreach
«for» с «yield» сделает 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 (sound <- SoundSource.getSound) { // Справа налево. SoundSource.getSound - начало выражения, sound - конец
  sound.play()                        // Слева направо
}

Вот так читабельнее:
SoundSource.getSound.foreach(sound => sound.play() ) // всё слево направо

А уж тем более проще
SoundSource.getSound.foreach(_.play() )
Я, похоже, непонятно выразился.

Это код из статьи:
for (sound <- SoundSource.getSound) {
  sound.play()
}

А вот это то, что по моему мнению будут все использовать в данном случае:
// аналогично
SoundSource.getSound.map{ _.play }

Почему map вместо foreach я ответил выше (в демонстративных целях).
Значит мы с вами в одной лодке. Да, я тоже часто пишу map для читабельности, даже когда имеется ввиду foreach. Прошу прощения что не так понял.
Если речь идёт о работе с коллекциями, то я с вами соглашусь – не всё так однозначно какой синтаксис более удобен. Но если речь о других типах, обладающих свойствами монада, то именно спец. синтаксис в виде «for» раскрывает всю красоту подхода. Для фьючеров, например, for-синтаксис намного более прозрачный, чем цепочки флэтмапов.
но ведь map и for не сработают, если там None.

я предпочитаю
someOpt.fold(defaultValue) {smthn => smthnWithSameTypeAsDefaultValue}
В статье и предполагается, что при None ничего делать не надо.

И да, я fold не люблю. Предпочитаю map + getOrElse. Читается лучше.
ну тут фломастеры разные. по мне так fold более краток

ирл зачастую приходится все же предпринимать какие-то действия в зависимости от того, пустой Option или нет.
кстати, в примере на джабахе все же вызывается play, пусть и пустой, но вызов происходит. может я завтра туда запихну заглушку какую или логгер.
так что это сравнение выходит не особо идентичным, что может некоторых смутить в начале пути.
Тогда это уже не Null object, а mock и возвращать надо Some
А что такое «обобщённое назначение», и почему это минус?
Зря написали про отсутствие минусов у кортежей и 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 }
Ограничение у case-классов в 22 члена пофиксано в Scala 2.11. А вот зачем в кортежах >22 членов — не очень понятно. Опишите пример из жизни?
Собственно именно это и было единственным примером из жизни в моём случае. Сейчас используем Squeryl.
Я довольно много пишу на Scala. Сравнение прикольное, но сложилось впечатление, что автор намеренно описал только те паттерны, где у Scala есть либо красивый встроенный синтаксический сахар, либо возможности, которых нет в Java. А так +1.
Остальные паттерны в scala хуже реализовываться точно не будут. Так что зачем показывать те, что реализовываются так же или те, для описания которых требуется много кода.

На самом деле почти все можно на скале сделать лаконичнее при помощи DSL, но не всегда это оправдывает написание отдельного DSL.
Предпочитаю в качестве адаптера использовать неявную функцию преобразования, но данное решение наиболее подходит если оба трейта или класса имеют реализации методов
Sign up to leave a comment.

Articles