Это вторая часть статьи. Первую часть читайте здесь.
Поведенческие паттерны
13. Chain of Responsibility (Цепочка обязанностей)
Описание: Позволяет передавать запросы последовательно по цепочке обработчиков.
Когда использовать: Когда есть более одного объекта, который может обработать запрос.
Пример кода:
abstract class Handler(private val next: Handler?) {
  open fun handle(request: String) {
    next?.handle(request)
  }
}
class AuthenticationHandler(next: Handler?) : Handler(next) {
  override fun handle(request: String) {
    if (request.contains("auth")) {
      println("Аутентификация прошла")
      super.handle(request)
    } else {
      println("Аутентификация не удалась")
    }
  }
}
class LoggingHandler(next: Handler?) : Handler(next) {
  override fun handle(request: String) {
    println("Логирование запроса: $request")
    super.handle(request)
  }
}
fun main() {
  val handler = AuthenticationHandler(LoggingHandler(null))
  handler.handle("auth: запрос к ресурсу")
}14. Command (Команда)
Описание: Инкапсулирует запрос как объект, позволяя параметризовать клиентов с разными запросами.
Когда использовать: Когда нужно параметризовать объекты выполняемым действием.
Пример кода:
interface Command {
  fun execute()
}
class Light {
  fun turnOn() = println("Свет включен")
  fun turnOff() = println("Свет выключен")
}
class TurnOnCommand(private val light: Light) : Command {
  override fun execute() = light.turnOn()
}
class TurnOffCommand(private val light: Light) : Command {
  override fun execute() = light.turnOff()
}
class RemoteControl {
  private val commands = mutableListOf<Command>()
  fun addCommand(command: Command) = commands.add(command)
  fun executeCommands() = commands.forEach { it.execute() }
}
fun main() {
  val light = Light()
  val turnOn = TurnOnCommand(light)
  val turnOff = TurnOffCommand(light)
  val remote = RemoteControl()
  remote.addCommand(turnOn)
  remote.addCommand(turnOff)
  remote.executeCommands()
}15. Iterator (Итератор)
Описание: Предоставляет способ последовательного доступа к элементам агрегатного объекта без раскрытия его внутреннего представления.
Когда использовать: Когда нужно предоставить единый интерфейс для обхода различных коллекций.
Пример кода:
class Notification(val message: String)
class NotificationCollection {
  private val notifications = mutableListOf<Notification>()
  fun addNotification(notification: Notification) = notifications.add(notification)
  fun iterator(): Iterator<Notification> = notifications.iterator()
}
fun main() {
  val collection = NotificationCollection()
  collection.addNotification(Notification("Уведомление 1"))
  collection.addNotification(Notification("Уведомление 2"))
  collection.addNotification(Notification("Уведомление 3"))
  val iterator = collection.iterator()
  while (iterator.hasNext()) {
    val notification = iterator.next()
    println(notification.message)
  }
}16. Mediator (Посредник)
Описание: Определяет объект, который инкапсулирует способ взаимодействия множества объектов.
Когда использовать: Когда нужно упростить коммуникацию между множеством взаимодействующих объектов.
Пример кода:
interface Mediator {
  fun notify(sender: Component, event: String)
}
abstract class Component(protected val mediator: Mediator)
class Button(mediator: Mediator) : Component(mediator) {
  fun click() {
    println("Кнопка нажата")
    mediator.notify(this, "click")
  }
}
class TextBox(mediator: Mediator) : Component(mediator) {
  fun setText(text: String) = println("Текстовое поле установлено в '$text'")
}
class AuthenticationDialog : Mediator {
  private val button = Button(this)
  private val textBox = TextBox(this)
  fun simulateUserAction() = button.click()
  override fun notify(sender: Component, event: String) {
    if (sender is Button && event = "click") {
      textBox.setText("Авторизация пользователя")
    }
  }
}
fun main() {
  val dialog = AuthenticationDialog()
  dialog.simulateUserAction()
}17. Memento (Хранитель)
Описание: Сохраняет внутреннее состояние объекта без нарушения инкапсуляции для возможности восстановления.
Когда использовать: Когда нужно сохранять и восстанавливать прошлые состояния объекта.
Пример кода:
class Editor {
  var content: String = ""
  fun save(): Memento = Memento(content)
  fun restore(memento: Memento) {
    content = memento.content
  }
  data class Memento(val content: String)
}
class History {
  private val states = mutableListOf<Editor.Memento>()
  fun push(memento: Editor.Memento) = states.add(memento)
  fun pop(): Editor.Memento = states.removeAt(states.lastIndex)
}
fun main() {
  val editor = Editor()
  val history = History()
  editor.content = "Состояние 1"
  history.push(editor.save())
  editor.content = "Состояние 2"
  history.push(editor.save())
  editor.content = "Состояние 3"
  editor.restore(history.pop())
  println("Текущее содержание: ${editor.content}")
  editor.restore(history.pop())
  println("Текущее содержание: ${editor.content}")
}18. Observer (Наблюдатель)
Описание: Определяет зависимость "один ко многим" между объектами так, что при изменении состояния одного объекта все зависящие от него оповещаются.
Когда использовать: Когда изменение состояния одного объекта требует изменения других объектов.
Пример кода:
interface Observer {
  fun update(state: Int)
}
class Subject {
  private val observers = mutableListOf<Observer>()
  var state: Int = 0
      set(value) {
        field = value
        notifyAllObservers()
      }
  fun attach(observer: Observer) = observers.add(observer) 
  private fun notifyAllObservers() = observers.forEach { it.update(state) }
}
class BinaryObserver : Observer {
  override fun update(state: Int) = 
      println("Двоичное представление: ${Integer.toBinaryString(state)}")
}
class HexObserver : Observer {
  override fun update(state: Int) = 
      println("Шестнадцатеричное представление: ${Integer.toHexString(state)}")
}
fun main() {
  val subject = Subject()
  subject.attach(BinaryObserver())
  subject.attach(HexObserver())
  subject.state = 15
  subject.state = 10
}19. State (Состояние)
Описание: Позволяет объекту менять свое поведение при изменении внутреннего состояния.
Когда использовать: Когда поведение объекта зависит от его состояния.
Пример кода:
interface State {
  fun handle(context: Context)
}
class Context {
  var state: State = ConcreteStateA()
  fun request() = state.handle(this)
}
class ConcreteStateA : State {
  override fun handle(context: Context) {
    println("Состояние А, переходим к В")
    context.state = ConcreteStateB()
  }
}
class ConcreteStateB : State {
  override fun handle(context: Context) {
    println("Состояние В, переходим к А")
    context.state = ConcreteStateA()
  }
}
fun main() {
  val context = Context()
  context.request()
  context.request()
  context.request()
}20. Strategy (Стратегия)
Описание: Определяет семейство алгоритмов, инкапсулирует каждый из них и обеспечивает их взаимозаменяемость.
Когда использовать: Когда есть несколько похожих алгоритмов, и нужно переключаться между ними во время выполнения.
Пример кода:
interface Strategy {
  fun execute(a: Int, b: Int): Int
}
class AdditionStrategy : Strategy {
  override fun execute(a: Int, b: Int): Int = a + b
}
class SubtractionStrategy : Strategy {
  override fun execute(a: Int, b: Int): Int = a - b
}
class Context(private var strategy: Strategy) {
  fun setStrategy(strategy: Strategy) {
    this.strategy = strategy
  }
  fun executeStrategy(a: Int, b: Int): Int = strategy.execute(a, b)
}
fun main() {
  val context = Context(AdditionStrategy())
  println("10 + 5 = ${context.executeStrategy(10, 5)}")
  context.setStrategy(SubtractionStrategy())
  println("10 - 5 = ${context.executeStrategy(10, 5)}")
}21. Template Method (Шаблонный метод)
Описание: Определяет скелет алгоритма в методе, оставляя реализацию шагов подклассам.
Когда использовать: Когда нужно определить основной алгоритм и делегировать реализацию отдельных шагов подклассам.
Пример кода:
abstract class Game {
  fun play() {
    initialize()
    startPlay()
    endPlay()
  }
  abstract fun initialize()
  abstract fun startPlay()
  abstract fun endPlay()
}
class Football : Game() {
  override fun initialize() = println("Футбол: Игра инициализирована")
  override fun startPlay() = println("Футбол: Игра начата")
  override fun endPlay() = println("Футбол: Игра завершена")
}
class Basketball : Game() {
  override fun initialize() = println("Баскетбол: Игра инициализирована")
  override fun startPlay() = println("Баскетбол: Игра начата")
  override fun endPlay() = println("Баскетбол: Игра завершена")
}
fun main() {
  val game1: Game = Football()
  game1.play()
  val game2: Game = Basketball()
  game2.play()
}22. Visitor (Посетитель)
Описание: Разделяет алгоритмы от структур данных, по которым они работают.
Когда использовать: Когда у вас есть сложная структура объектов, и вы хотите выполнять над ними разнообразные операции, не изменяя классы этих объектов.
Пример кода:
// Элемент, который принимает посетителя
interface Shape {
    fun accept(visitor: Visitor)
}
// Конкретные элементы
class Circle(val radius: Double) : Shape {
    override fun accept(visitor: Visitor) {
        visitor.visitCircle(this)
    }
}
class Rectangle(val width: Double, val height: Double) : Shape {
    override fun accept(visitor: Visitor) {
        visitor.visitRectangle(this)
    }
}
// Интерфейс посетителя
interface Visitor {
    fun visitCircle(circle: Circle)
    fun visitRectangle(rectangle: Rectangle)
}
// Посетитель для рисования фигур
class DrawVisitor : Visitor {
    override fun visitCircle(circle: Circle) {
        println("Рисуем круг с радиусом ${circle.radius}")
    }
    override fun visitRectangle(rectangle: Rectangle) {
        println("Рисуем прямоугольник шириной ${rectangle.width} и высотой ${rectangle.height}")
    }
}
// Посетитель для вычисления площади
class AreaVisitor : Visitor {
    override fun visitCircle(circle: Circle) {
        val area = Math.PI * circle.radius * circle.radius
        println("Площадь круга: $area")
    }
    override fun visitRectangle(rectangle: Rectangle) {
        val area = rectangle.width * rectangle.height
        println("Площадь прямоугольника: $area")
    }
}
fun main() {
    val shapes = listOf<Shape>(
        Circle(5.0),
        Rectangle(3.0, 4.0)
    )
    val drawVisitor = DrawVisitor()
    val areaVisitor = AreaVisitor()
    // Рисуем фигуры
    println("=== Рисование фигур ===")
    shapes.forEach { it.accept(drawVisitor) }
    // Вычисляем площади фигур
    println("\n=== Вычисление площади фигур ===")
    shapes.forEach { it.accept(areaVisitor) }
}Это основные паттерны проектирования с примерами на языке Kotlin. Каждый паттерн решает определенную проблему и может быть использован в различных ситуациях для улучшения архитектуры приложения.
 
           
 
ris58h
Не надо половину статьи в превью вставлять.
FayzullinFF Автор
Благодарю. Исправил.