Паттерны проектирования — проверенные временем решения общих задач в программировании. Они разделяются на три категории:
-
Порождающие (Creational)
-
Структурные (Structural)
-
Поведенческие (Behavioral)
В этой части статьи рассмотрим порождающие и структурные паттерны.
Порождающие паттерны
1. Singleton (Одиночка)
Описание: Гарантирует, что у класса есть только один экземпляр, и предоставляет к нему глобальную точку доступа.
Когда использовать: Когда нужен единственный экземпляр класса для контроля доступа к общему ресурсу.
Пример кода:
object DatabaseConnection { fun connect() { println("Подключение к базе данных") } } fun main() { DatabaseConnection.connect() }
Объяснение: в Kotlin есть ключевое слово object, которое автоматически создает синглтон.
2. Factory Method (Фабричный метод)
Описание: Определяет интерфейс для создания объектов, но позволяет подклассам решать, какой класс инстанцировать.
Когда использовать: Когда заранее неизвестны типы и зависимости объектов, с которыми должен работать ваш код.
Пример кода:
interface Transport { fun deliver() } class Truck : Transport { override fun deliver() { println("Доставка грузовиком по суше") } } class Ship : Transport { override fun deliver() { println("Доставка кораблем по морю") } } abstract class Logistics { abstract fun createTransport(): Transport fun planDelivery() { val transport = createTransport() transport.deliver() } } class RoadLogistics : Logistics() { override fun createTransport(): Transport = Truck() } class SeaLogistics: Logistics() { override fun createTransport = Ship() } fun main() { val logistics: Logistics = RoadLogistics() logistics.planDelivery() }
3. Abstract Factory (Абстрактная фабрика)
Описание: Предоставляет интерфейс для создания семейств связанных или зависимых объектов без указания их конкретных классов.
Когда использовать: Когда система должна быть независимой от процесса создания, композиции и представления продуктов.
Пример кода:
interface Button { fun render() } interface Checkbox { fun render() } class WindowsButton : Button { override fun render() { println("Рендеринг кнопки в стиле Windows") } } class MacOSButton : Button { override fun render() { println("Рендеринг кнопки в стиле MacOS") } } class WindowsCheckbox : Checkbox { override fun render() { println("Рендеринг чекбокса в стиле Windows") } } class MacOSCheckbox : Checkbox { override fun render() { println("Рендеринг чекбокса в стиле MacOS") } } interface GUIFactory { fun createButton(): Button fun createCheckbox(): Checkbox } class WindowsFactory : GUIFactory { override fun createButton(): Button = WindowsButton() override fun createCheckbox(): Checkbox = WindowsCheckbox() } class MacOSFactory : GUIFactory { override fun createButton(): Button = MacOSButton() override fun createCheckbox(): Checkbox = MacOSCheckbox() } fun main() { val factory: GUIFactory = WindowsFactory() val button = factory.createButton() val checkbox = factory.createCheckbox() button.render() checkbox.render() }
4. Builder (Строитель)
Описание: Разделяет создание сложного объекта от его представления, так что один и тот же процесс строительства может создавать разные представления.
Когда использовать: Когда процесс создания объекта должен быть независим от его составляющих и как они собираются.
Пример кода:
class House private constructor( val walls: Int, val doors: Int, val windows: Int, val hasGarage: Boolean, val hasSwimmingPool: Boolean ) { data class Builder( var walls: Int = 0, var doors: Int = 0, var windows: Int = 0, var hasGarage: Boolean = false, var hasSwimmingPool: Boolean = false ) { fun walls(count: Int) = apply { this.walls = count } fun doors(count: Int) = apply { this.doors = count } fun windows(count: Int) = apply { this.windows = count } fun hasGarage(value: Boolean) = apply { this.hasGarage = value } fun hasSwimmingPool(value: Boolean) = apply { this.hasSwimmingPool = value } fun build() = House(walls, doors, windows, hasGarage, hasSwimmingPool) } } fun main() { val house = House.Builder() .walls(4) .doors(2) .windows(6) .hasGarage(true) .build() println("Дом с ${house.walls} стенами, ${house.doors} дверями, " + "${house.windows} окнами, гараж: ${house.hasGarage}") }
5. Prototype (Прототип)
Описание: Позволяет копировать объекты не вдаваясь в подробности их реализации.
Когда использовать: Когда создание объекта дорогостоящее или сложно, а копирование может быть более эффективным.
Пример кода:
abstract class Shape : Cloneable { var x: Int = 0 var y: Int = 0 public override fun clone(): Shape { return super.clone() as Shape } abstract fun draw() } class Rectangle(var width: Int, var height: Int) : Shape() { override fun clone(): Shape { val clone = super.clone() as Rectangle clone.width = this.width clone.height = this.height return clone } override fun draw() { println("Рисуем прямоугольник шириной $width и высотой $height") } } fun main() { val original = Rectangle(10, 20) val copy = original.clone() as Rectangle copy.width = 30 original.draw() copy.draw() }
Структурные паттерны
6. Adapter (Адаптер)
Описание: Позволяет объектам с несовместимыми интерфейсами работать вместе.
Когда использовать: Когда нужно использовать существующий класс, но его интерфейс не соответствует потребностям.
Пример кода:
interface RoundPeg { val radius: Double } class RoundHole(val radius: Double) { fun fits(peg: RoundPeg): Boolean = this.radius >= peg.radius } class SquarePeg(val width: Double) class SquarePegAdapter(private val peg: SquarePeg) : RoundPeg { override val radius: Double get() = peg.width * Math.sqrt(2.0) / 2 } fun main() { val hole = RoundHole(5.0) val smallSquarePeg = SquarePeg(5.0) val largeSquarePeg = SquarePeg(10.0) val smallPegAdapter = SquarePegAdapter(smallSquarePeg) val largePegAdapter = SquarePegAdapter(largeSquarePeg) println("Малый квадратный колышек подходит? ${hole.fits(smallPegAdapter)}") println("Большой квадратный колышек подходит? ${hole.fits(largePegAdapter)}") }
7. Bridge (Мост)
Описание: Разделяет абстракцию и реализацию так, чтобы они могли изменяться независимо.
Когда использовать: Когда нужно разделить монолитный класс на несколько отдельных иерархий.
Пример кода:
interface Device { var volume: Int var isEnabled: Boolean fun enable() fun disable() } class Radio : Device { override var volume: Int = 30 override var isEnabled: Boolean = false override fun enable() { isEnabled = true println("Радио включено") } override fun disable() { isEnabled = false println("Радио выключено") } } class TV : Device { override var volume: Int = 50 override var isEnabled: Boolean = false override fun enable() { isEnabled = true println("Телевизор включен") } override fun disable() { isEnabled = false println("Телевизор выключен") } } abstract class Remote(val device: Device) { fun togglePower() { if (device.isEnabled) { device.disable() } else { device.enable() } } fun volumeUp() { device.volume += 10 println("Громкость увеличена до ${device.volume}") } fun volumeDown() { device.volume -= 10 println("Громкость уменьшена до ${device.volume}") } } class AdvancedRemote(device: Device) : Remote(device) { fun mute() { device.volume = 0 println("Звук выключен") } } fun main() { val tv = TV() val remote = AdvancedRemote(tv) remote.togglePower() remote.volumeUp() remote.mute() }
8. Composite (Компоновщик)
Описание: Позволяет создавать древовидные структуры объектов и работать с ними как с единичными объектами.
Когда использовать: Когда нужно представить иерархию объектов и работать с ними единообразно.
Пример кода:
interface Graphic { fun draw() } class Dot(val x: Int, val y: Int) : Graphic { override fun draw() { println("Рисуем точку на координатах ($x, $y)") } } class Circle(x: Int, y: Int, val radius: Int) : Dot(x, y) { override fun draw() { println("Рисуем круг с центром ($x, $y) и радиусом $radius") } } class CompoundGraphic : Graphic { private val children = mutableListOf<Graphic>() fun add(child: Graphic) = children.add(child) fun remove(child: Graphic) = children.remove(child) override fun draw() { println("Рисуем составной график") children.forEach { it.draw() } } } fun main() { val dot = Dot(1, 2) val circle = Circle(5, 3, 10) val compoundGraphic = CompoundGraphic() compoundGraphic.add(dot) compoundGraphic.add(circle) compoundGraphic.draw() }
9. Decorator (Декоратор)
Описание: Динамически добавляет объектам новые обязанности.
Когда использовать: Когда нужно добавить обязанности объекту, не затрагивая другие объекты.
Пример кода:
interface DataSource { fun writeData(data: String) fun readData(): String } class FileDataSource(private val filename: String) : DataSource { private var data: String = "" override fun writeData(data: String) { this.data = data println("Данные записаны в файл $filename") } override fun readData(): String { println("Чтение данных из файла $filename") return data } } open class DataSourceDecorator(private val wrappee: DataSource) : DataSource { override fun writeData(data: String) { wrappee.writeData(data) } override fun readData(): String = wrappee.readData() } class EncryptionDecorator(wrappee: DataSource) : DataSourceDecorator(wrappee) { override fun writeData(data: String) { val encryptedData = "encrypted($data)" super.writeData(encryptedData) } override fun readData(): String { val data = super.readData() return "decrypted($data)" } } fun main() { val source = FileDataSource("somefile.dat") val encryptedSource = EncryptionDecorator(source) encryptedSource.writeData("важные данные") println(encryptedSource.readData()) }
10. Facade (Фасад)
Описание: Предоставляет унифицированный интерфейс к набору интерфейсов в системе.
Когда использовать: Когда нужно упростить взаимодействие с комплексной системой.
Пример кода:
class CPU { fun jump(position: Long) = println("CPU переходит к $position") fun execute() = println("CPU выполняет инструкции") } class Memory { fun load(position: Long, data: String) = println("Память загружает данные '$data' на позицию $position") } class HardDrive { fun read(lba: Long, size: Int): String { println("Жесткий диск читает $size байт с позиции $lba") return "данные" } } class ComputerFacade { private val cpu = CPU() private val memory = Memory() private val hardDrive = HardDrive() fun start() { val bootData = hardDrive.read(0, 1024) memory.load(0, bootData) cpu.jump(0) cpu.execute() } } fun main() { val computer = ComputerFacade() computer.start() }
11. Flyweight (Приспособленец)
Описание: Позволяет вместить большее количество объектов, используя разделение общего состояния между ними.
Когда использовать: Когда приложение должно эффективно подддерживать множество мелких объектов.
Пример кода:
data class TreeType(val name: String, val color: string, val texture: String) class Tree(val x: Int, val y: Int, val type: TreeType) { fun draw() = println("Рисуем дерево '${type.name}' на позиции ($x, $y)") } object TreeFactory { private val treeTypes = mutableMapOf<String, TreeType>() fun getTreeType(name: String, color: String, texture: String): TreeType { val key = "$name-$color-$texture" return treeTypes.getOrPut(key) { TreeType(name, color, texture) } } } class Forest { private val trees = mutableListOf<Tree>() fun plantTree(x: Int, y: Int, name: String, color: String, texture: String) { val type = TreeFactory.getTreeType(name, color, texture) val tree = Tree(x, y, type) trees.add(tree) } fun draw() = trees.forEach { it.draw() } } fun main() { val forest = Forest() forest.plantTree(1, 2, "Дуб", "Зеленый", "Грубая") forest.plantTree(3, 4, "Дуб", "Зеленый", "Грубая") forest.plantTree(5, 6, "Береза", "Белый", "Гладкая") forest.draw() }
12. Proxy (Заместитель)
Описание: Предоставляет суррогатный объект, контролирующий доступ к другому объекту.
Когда использовать: Когда нужен более функциональный или изолированный доступ к объекту.
Пример кода:
interface Image { fun display() } class RealImage(private val filename: String) : Image { init { loadFromDisk() } private fun loadFromDisk() = println("Загрузка $filename с диска") override fun display() = println("Отображение $filename") } class ProxyImage(private val filename: String) : Image { private var realImage: RealImage? = null override fun display() { if (realImage == null) { realImage = RealImage(filename) } realImage?.display() } } fun main() { val image = ProxyImage("test_image.jpg") image.display() // загрузка произойдет здесь image.display() // загрузка не произойдет }
В следующей части статьи рассмотрим поведенческие паттерны.
ссылка на оригинал статьи https://habr.com/ru/articles/859520/
Добавить комментарий