Паттерны проектирования на языке Kotlin

от автора

Это вторая часть статьи. Первую часть читайте здесь.

Поведенческие паттерны

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. Каждый паттерн решает определенную проблему и может быть использован в различных ситуациях для улучшения архитектуры приложения.


ссылка на оригинал статьи https://habr.com/ru/articles/860030/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *