- Принцип единственной ответственности (SRP)
- Принцип открытости/закрытости (OCP)
- Принцип подстановки Лисков (LSP)
- Принцип разделения интерфейсов (ISP)
- Принцип инверсии зависимостей (DIP)
Класс должен иметь только одну причину для изменения.
Пример:
// ❌ Нарушение SRP
class User {
fun saveUser() { /* сохранение в БД */ }
fun logUserAction() { /* логирование */ }
}
// ✅ Соблюдение SRP
class UserRepository {
fun save() { /* сохранение в БД */ }
}
class Logger {
fun logAction() { /* логирование */ }
}
Сущности должны быть открыты для расширения, но закрыты для модификации.
Пример:
// ❌ Нарушение 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
}
Подтипы должны быть заменяемы базовыми типами без изменения корректности программы.
Пример:
// ❌ Нарушение 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
}
Много специализированных интерфейсов лучше одного универсального.
Пример:
// ❌ Нарушение 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() { /* ... */ }
}
Зависимости должны строиться на абстракциях, а не на конкретных реализациях.
Пример:
// ❌ Нарушение 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)
}
}