Ответ 1
Для почти всех из них существуют альтернативы Scala, которые охватывают некоторые, но не все варианты использования этих шаблонов. Все это ИМО, конечно, но:
Шаблоны создания
Builder
Scala может сделать это более элегантно с родовыми типами, чем Java, но общая идея такая же. В Scala шаблон наиболее просто реализуется следующим образом:
trait Status
trait Done extends Status
trait Need extends Status
case class Built(a: Int, b: String) {}
class Builder[A <: Status, B <: Status] private () {
private var built = Built(0,"")
def setA(a0: Int) = { built = built.copy(a = a0); this.asInstanceOf[Builder[Done,B]] }
def setB(b0: String) = { built = built.copy(b = b0); this.asInstanceOf[Builder[A,Done]] }
def result(implicit ev: Builder[A,B] <:< Builder[Done,Done]) = built
}
object Builder {
def apply() = new Builder[Need, Need]
}
(Если вы попробуете это в REPL, убедитесь, что класс и объект Builder определены в одном блоке, то есть используйте :paste
.) Сочетание типов проверки с <:<
, аргументами общего типа и метод копирования классов case делает очень мощную комбинацию.
Factory Метод (и абстрактный метод Factory)
Factory Основное использование методов - держать ваши типы прямыми; в противном случае вы можете использовать конструкторы. При использовании Scala мощной системы типов вам не нужна помощь, позволяющая держать ваши типы прямыми, поэтому вы можете использовать конструктор или метод apply
в объекте-компаньоне для вашего класса и создавать вещи таким образом. В частности, в случае сопутствующего объекта не сложнее сохранить согласованный интерфейс, чем поддерживать согласованный интерфейс в объекте Factory. Таким образом, большая часть мотивации для объектов Factory отсутствует.
Аналогично, многие случаи абстрактных методов Factory могут быть заменены наличием сопутствующего объекта, наследуемого от соответствующего признака.
Прототип
Конечно, переопределенные методы и тому подобное имеют место в Scala. Тем не менее, примеры, используемые для шаблона Prototype на веб-сайте Design Patterns, скорее нецелесообразны в Scala (или Java IMO). Однако, если вы хотите, чтобы суперклассы выбирали действия на основе своих подклассов, а не позволяли им самим решать, вы должны использовать match
, а не неуклюжие тесты instanceof
.
Singleton
Scala охватывает их с помощью object
. Это одиночные игры - используйте и наслаждайтесь!
Структурные шаблоны
Адаптер
Scala trait
обеспечивает гораздо большую мощность здесь - вместо создания класса, который реализует интерфейс, например, вы можете создать признак, который реализует только часть интерфейса, оставив для вас остальное. Например, java.awt.event.MouseMotionListener
требует, чтобы вы заполнили два метода:
def mouseDragged(me: java.awt.event.MouseEvent)
def mouseMoved(me: java.awt.event.MouseEvent)
Возможно, вы хотите игнорировать перетаскивание. Затем вы пишете trait
:
trait MouseMoveListener extends java.awt.event.MouseMotionListener {
def mouseDragged(me: java.awt.event.MouseEvent) {}
}
Теперь вы можете реализовать только mouseMoved
, когда вы наследуете это. Итак: аналогичный шаблон, но гораздо больше мощности с Scala.
Мост
Вы можете писать мосты в Scala. Это огромное количество шаблонов, хотя и не так плохо, как на Java. Я бы не рекомендовал регулярно использовать это как метод абстракции; сначала подумайте о своих интерфейсах. Имейте в виду, что с повышенной способностью черт, которые вы часто можете использовать, чтобы упростить более сложный интерфейс в месте, где в противном случае может возникнуть соблазн написать мост.
В некоторых случаях вы можете написать трансформатор интерфейса вместо шаблона моста Java. Например, возможно, вы хотите обрабатывать перетаскивания и перемещения мыши, используя один и тот же интерфейс, только с булевым флагом, отличающим их. Тогда вы можете
trait MouseMotioner extends java.awt.event.MouseMotionListener {
def mouseMotion(me: java.awt.event.MouseEvent, drag: Boolean): Unit
def mouseMoved(me: java.awt.event.MouseEvent) { mouseMotion(me, false) }
def mouseDragged(me: java.awt.event.MouseEvent) { mouseMotion(me, true) }
}
Это позволяет вам пропускать большую часть шаблона шаблона моста при одновременном достижении высокой степени независимости реализации и по-прежнему позволяя вашим классам подчиняться исходному интерфейсу (так что вам не нужно их обертывать и разворачивать).
Composite
Сложный шаблон особенно легко получить с помощью классов case, хотя внесение обновлений довольно сложно. Это одинаково ценно в Scala и Java.
декоратор
Декораторы неудобны. Обычно вы не хотите использовать одни и те же методы в другом классе в случае, когда наследование не совсем то, что вы хотите; то, что вы действительно хотите, это другой метод в том же классе, который делает то, что вы хотите, а не по умолчанию. рисунок обогащения-my-library часто является превосходной заменой.
Фасад
Фасад работает лучше в Scala, чем на Java, потому что вы можете иметь черты, несущие частичные реализации, поэтому вам не нужно выполнять всю работу самостоятельно, когда вы их объединяете.
Наименьший вес
Несмотря на то, что идея мухи столь же важна в Scala как Java, у вас есть еще несколько инструментов для ее реализации: lazy val
, где переменная не создается, если она фактически не нужна (и после этого используется повторно), и by-name parameters
, где вы выполняете только работу, необходимую для создания аргумента функции, если функция действительно использует это значение. Тем не менее, в некоторых случаях шаблон Java остается неизменным.
Proxy
Работает аналогично в Scala как Java.
Поведенческие шаблоны
Цепочка ответственности
В тех случаях, когда вы можете указать ответственные стороны в порядке, вы можете
xs.find(_.handleMessage(m))
предполагая, что каждый имеет метод handleMessage
, который возвращает true
, если сообщение было обработано. Если вы хотите мутировать сообщение, как оно есть, используйте вместо этого клавишу fold.
Так как легко отбрасывать ответственные стороны в Buffer
какого-то рода, сложная структура, используемая в Java-решениях, редко имеет место в Scala.
Command
Этот шаблон почти полностью заменяется функциями. Например, вместо всех
public interface ChangeListener extends EventListener {
void stateChanged(ChangeEvent e)
}
...
void addChangeListener(ChangeListener listener) { ... }
вы просто
def onChange(f: ChangeEvent => Unit)
Переводчик
Scala предоставляет компараторы парсеров, которые значительно мощнее, чем простой интерпретатор, предложенный в качестве шаблона проектирования.
Итератор
Scala имеет Iterator
встроенный в свою стандартную библиотеку. Практически тривиально сделать свой собственный класс Iterator
или Iterable
; последний обычно лучше, поскольку он делает повторное использование тривиальным. Определенно хорошая идея, но так просто, я бы не назвал ее шаблоном.
Mediator
Это отлично работает в Scala, но, как правило, полезно для изменяемых данных, и даже посредники могут падать в зависимости от условий гонки и, если не использовать их осторожно. Вместо этого попробуйте, когда возможно, чтобы ваши связанные данные были сохранены в одной неизменяемой коллекции, класс case или что-то еще, и при создании обновления, требующего скоординированных изменений, одновременно изменить все вещи. Это не поможет вам взаимодействовать с javax.swing
, но в остальном широко применяется:
case class Entry(s: String, d: Double, notes: Option[String]) {}
def parse(s0: String, old: Entry) = {
try { old.copy(s = s0, d = s0.toDouble) }
catch { case e: Exception => old }
}
Сохраните шаблон посредника, когда вам нужно обрабатывать несколько разных отношений (по одному медиатору для каждого) или когда у вас есть изменяемые данные.
Memento
lazy val
почти идеально подходит для многих простейших приложений шаблона памяти, например
class OneRandom {
lazy val value = scala.util.Random.nextInt
}
val r = new OneRandom
r.value // Evaluated here
r.value // Same value returned again
Вы можете создать небольшой класс специально для ленивой оценки:
class Lazily[A](a: => A) {
lazy val value = a
}
val r = Lazily(scala.util.Random.nextInt)
// not actually called until/unless we ask for r.value
Observer
В лучшем случае это хрупкий шаблон. Поощряйте, когда это возможно, сохранение неизменяемого состояния (см. "Посредник" ) или использование актеров, в которых один актер отправляет сообщения всем остальным о смене штата, но где каждый актер может справиться с устаревшим.
Государственные
Это одинаково полезно в Scala и на самом деле является предпочтительным способом создания перечислений при применении к методам без меток:
sealed trait DayOfWeek
final trait Sunday extends DayOfWeek
...
final trait Saturday extends DayOfWeek
(часто вы хотите, чтобы будни делали что-то, чтобы оправдать это количество шаблонов).
Стратегия
Это почти полностью заменено тем, что методы выполняют функции, реализующие стратегию, и предоставляют функции для выбора.
def printElapsedTime(t: Long, rounding: Double => Long = math.round) {
println(rounding(t*0.001))
}
printElapsedTime(1700, math.floor) // Change strategy
Метод шаблона
Черты предлагают гораздо больше возможностей, поэтому лучше всего рассмотреть их еще один образец. Вы можете заполнить столько кода, сколько сможете, из той же информации, что и на уровне абстракции. Я бы не хотел называть это одним и тем же.
Visitor
Между структурная типизация и неявное преобразование, Scala обладает потрясающе большим количеством возможностей, чем типичный шаблон посетителя Java. Нет смысла использовать оригинальный шаблон; вы просто отвлекитесь от правильного пути, чтобы сделать это. Многие из примеров действительно просто хотят, чтобы была функция, определенная на посещаемой вещи, которую Scala может сделать для вас тривиально (т.е. Преобразовать произвольный метод в функцию).