Skip to content

Instantly share code, notes, and snippets.

@dmitry-osin
Created January 25, 2025 11:17
Show Gist options
  • Save dmitry-osin/9546ff16813de32a47182a30cd61f798 to your computer and use it in GitHub Desktop.
Save dmitry-osin/9546ff16813de32a47182a30cd61f798 to your computer and use it in GitHub Desktop.

Принципы SOLID

Оглавление

  1. Принцип единственной ответственности (SRP)
  2. Принцип открытости/закрытости (OCP)
  3. Принцип подстановки Лисков (LSP)
  4. Принцип разделения интерфейсов (ISP)
  5. Принцип инверсии зависимостей (DIP)

1. Принцип единственной ответственности (SRP)

Класс должен иметь только одну причину для изменения.

Пример:

// ❌ Нарушение SRP
class User {
    fun saveUser() { /* сохранение в БД */ }
    fun logUserAction() { /* логирование */ }
}

// ✅ Соблюдение SRP
class UserRepository {
    fun save() { /* сохранение в БД */ }
}

class Logger {
    fun logAction() { /* логирование */ }
}

2. Принцип открытости/закрытости (OCP)

Сущности должны быть открыты для расширения, но закрыты для модификации.

Пример:

// ❌ Нарушение OCP
class AreaCalculator {
    fun calculate(shape: Any): Double {
        return when (shape) {
            is Circle -> Math.PI * shape.radius.pow(2)
            is Square -> shape.side * shape.side
            else -> throw IllegalArgumentException()
        }
    }
}

// ✅ Соблюдение OCP
interface Shape {
    fun area(): Double
}

class Circle(val radius: Double) : Shape {
    override fun area() = Math.PI * radius.pow(2)
}

class Square(val side: Double) : Shape {
    override fun area() = side * side
}

3. Принцип подстановки Лисков (LSP)

Подтипы должны быть заменяемы базовыми типами без изменения корректности программы.

Пример:

// ❌ Нарушение LSP
open class Rectangle(val width: Double, val height: Double) {
    open fun setWidth(w: Double) { /* ... */ }
    open fun setHeight(h: Double) { /* ... */ }
}

class Square : Rectangle(0.0, 0.0) {
    override fun setWidth(w: Double) {
        super.setWidth(w)
        super.setHeight(w) // Нарушение: изменяет поведение базового класса
    }
}

// ✅ Соблюдение LSP
interface Shape {
    fun area(): Double
}

class Rectangle(val width: Double, val height: Double) : Shape {
    override fun area() = width * height
}

class Square(val side: Double) : Shape {
    override fun area() = side * side
}

4. Принцип разделения интерфейсов (ISP)

Много специализированных интерфейсов лучше одного универсального.

Пример:

// ❌ Нарушение ISP
interface Worker {
    fun work()
    fun eat()
}

class Robot : Worker {
    override fun work() { /* ... */ }
    override fun eat() { throw Exception("Робот не ест!") } // Принудительная реализация
}

// ✅ Соблюдение ISP
interface Workable {
    fun work()
}

interface Eatable {
    fun eat()
}

class Human : Workable, Eatable {
    override fun work() { /* ... */ }
    override fun eat() { /* ... */ }
}

class Robot : Workable {
    override fun work() { /* ... */ }
}

5. Принцип инверсии зависимостей (DIP)

Зависимости должны строиться на абстракциях, а не на конкретных реализациях.

Пример:

// ❌ Нарушение DIP
class MySQLDatabase {
    fun saveData(data: String) { /* ... */ }
}

class Service {
    private val db = MySQLDatabase() // Жесткая зависимость
    fun process(data: String) {
        db.saveData(data)
    }
}

// ✅ Соблюдение DIP
interface Database {
    fun save(data: String)
}

class MySQLDatabase : Database {
    override fun save(data: String) { /* ... */ }
}

class Service(private val db: Database) { // Внедрение зависимости через конструктор
    fun process(data: String) {
        db.save(data)
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment