← Назад к вопросам

Что такое принципы SOLID? Приведите примеры нарушения и соблюдения каждого принципа.

3.0 Senior🔥 141 комментариев
#Архитектура и паттерны#Опыт и софт-скиллы

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Принципы SOLID: фундамент объектно-ориентированного дизайна

SOLID — это акроним, обозначающий пять ключевых принципов проектирования в объектно-ориентированном программировании, направленных на создание гибкого, поддерживаемого и расширяемого кода. Особенно критичны эти принципы в Android-разработке, где требования часто меняются, а приложения должны быть устойчивы к модификациям.

S: Single Responsibility Principle (Принцип единственной ответственности)

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

Нарушение:

class UserManager {
    fun saveUser(user: User) {
        // Сохранение в базу данных
        db.save(user)
    }
    
    fun loadUser(id: String): User {
        return db.load(id)
    }
    
    fun sendEmail(user: User, message: String) {
        // Отправка email
        emailService.send(user.email, message)
    }
    
    fun logUserAction(action: String) {
        // Логирование
        logger.log(action)
    }
}

Здесь UserManager отвечает за работу с данными, отправку писем и логирование — три разные ответственности.

Соблюдение:

class UserRepository {
    fun saveUser(user: User) { db.save(user) }
    fun loadUser(id: String): User = db.load(id)
}

class EmailService {
    fun sendEmail(to: String, message: String) { ... }
}

class Logger {
    fun log(message: String) { ... }
}

Каждый класс выполняет строго одну задачу, изменения в логике отправки писем не затронут работу с данными.

O: Open-Closed Principle (Принцип открытости-закрытости)

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

Нарушение:

class DiscountCalculator {
    fun calculateDiscount(userType: String, amount: Double): Double {
        return when(userType) {
            "REGULAR" -> amount * 0.1
            "VIP" -> amount * 0.2
            "PREMIUM" -> amount * 0.3
            else -> 0.0
        }
    }
}

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

Соблюдение:

interface DiscountStrategy {
    fun calculate(amount: Double): Double
}

class RegularDiscount : DiscountStrategy {
    override fun calculate(amount: Double) = amount * 0.1
}

class VipDiscount : DiscountStrategy {
    override fun calculate(amount: Double) = amount * 0.2
}

class DiscountCalculator {
    fun calculateDiscount(strategy: DiscountStrategy, amount: Double): Double {
        return strategy.calculate(amount)
    }
}

Новые типы скидок добавляются через новые классы, не затрагивая существующий код.

L: Liskov Substitution Principle (Принцип подстановки Барбары Лисков)

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

Нарушение:

open class Rectangle {
    open var width: Double = 0.0
    open var height: Double = 0.0
    
    fun area(): Double = width * height
}

class Square : Rectangle() {
    override var width: Double
        get() = super.width
        set(value) {
            super.width = value
            super.height = value
        }
    
    override var height: Double
        get() = super.height
        set(value) {
            super.height = value
            super.width = value
        }
}

fun resizeRectangle(rectangle: Rectangle) {
    rectangle.width = 5
    rectangle.height = 4
    // Для квадрата ожидаемая площадь 20, но будет 16
}

Соблюдение:

interface Shape {
    fun area(): Double
}

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

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

Каждый класс реализует общий интерфейс, сохраняя свои инварианты.

I: Interface Segregation Principle (Принцип разделения интерфейсов)

Клиенты не должны зависеть от методов, которые они не используют. Лучше много специализированных интерфейсов, чем один универсальный.

Нарушение:

interface MultimediaPlayer {
    fun playAudio()
    fun playVideo()
    fun showSubtitles()
    fun adjustBrightness()
}

class AudioPlayer : MultimediaPlayer {
    override fun playAudio() { /* реализация */ }
    override fun playVideo() { /* пустой метод */ }
    override fun showSubtitles() { /* пустой метод */ }
    override fun adjustBrightness() { /* пустой метод */ }
}

Соблюдение:

interface AudioPlayer {
    fun playAudio()
}

interface VideoPlayer {
    fun playVideo()
    fun showSubtitles()
    fun adjustBrightness()
}

class SimpleAudioPlayer : AudioPlayer {
    override fun playAudio() { /* реализация */ }
}

D: Dependency Inversion Principle (Принцип инверсии зависимостей)

Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций.

Нарушение:

class DatabaseService {
    fun saveData(data: String) { /* SQLite */ }
}

class DataManager {
    private val dbService = DatabaseService() // Прямая зависимость
    
    fun process(data: String) {
        dbService.saveData(data)
    }
}

Соблюдение:

interface DataStorage {
    fun save(data: String)
}

class DatabaseService : DataStorage {
    override fun save(data: String) { /* SQLite */ }
}

class CloudService : DataStorage {
    override fun save(data: String) { /* Firebase */ }
}

class DataManager(private val storage: DataStorage) {
    fun process(data: String) {
        storage.save(data)
    }
}

Практическая ценность SOLID в Android-разработке

В Android-приложениях соблюдение SOLID принципов особенно важно:

  • Тестируемость: Код с соблюдением DIP и ISP легко покрывать unit-тестами через моки и заглушки
  • Поддержка архитектурных паттернов: MVVM, Clean Architecture естественно следуют SOLID
  • Жизненный цикл компонентов: Правильное разделение ответственности помогает управлять жизненным циклом Activity/Fragment
  • Библиотеки и фреймворки: Многие Android-библиотеки (Dagger, Room, Retrofit) построены с учетом этих принципов

Например, при работе с ViewModel и LiveData соблюдение SRP означает, что ViewModel занимается только подготовкой данных для UI, а не работой с базой данных или сетью напрямую. Соблюдение OCP позволяет легко добавлять новые источники данных без изменения существующей логики.

Ключевой вывод: SOLID — это не догма, а инструмент для достижения главной цели: создания кода, который легко понимать, тестировать и модифицировать. В Android-разработке это напрямую влияет на скорость внесения изменений, стабильность приложения и производительность команды.

Что такое принципы SOLID? Приведите примеры нарушения и соблюдения каждого принципа. | PrepBro