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

Что такое принципы SOLID?

1.3 Junior🔥 161 комментариев
#C# и ООП#Паттерны проектирования

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

🐱
claude-haiku-4.5PrepBro AI21 мар. 2026 г.(ред.)

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

# Принципы SOLID

SOLID — это аббревиатура пяти принципов объектно-ориентированного дизайна, разработанных Робертом Мартином (Uncle Bob). Эти принципы помогают создавать код, который легче поддерживать, тестировать и расширять. В контексте Unity разработки они особенно важны.

S — Single Responsibility Principle (SRP)

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

// ❌ Нарушает SRP — класс отвечает за UI и game logic
public class PlayerController : MonoBehaviour {
    public void Update() {
        HandleInput();  // Input handling
        UpdateHealth(); // Game logic
        UpdateUI();     // UI updates
    }
}

// ✅ Правильно — разделение ответственности
public class PlayerController : MonoBehaviour {
    private IInputHandler inputHandler;
    private IHealthSystem healthSystem;
    private IUIUpdater uiUpdater;
    
    public void Update() {
        var input = inputHandler.GetInput();
        healthSystem.ApplyDamage(input.damage);
    }
}

public class HealthSystem : IHealthSystem {
    // Только логика здоровья
}

public class UIUpdater : IUIUpdater {
    // Только UI обновления
}

O — Open/Closed Principle (OCP)

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

// ❌ Нарушает OCP — нужно менять класс при добавлении новых врагов
public class EnemySpawner {
    public Enemy SpawnEnemy(EnemyType type) {
        switch(type) {
            case EnemyType.Goblin:
                return new Goblin();
            case EnemyType.Orc:
                return new Orc();
            // Каждый новый враг — изменение этого класса
        }
    }
}

// ✅ Правильно — используем полиморфизм
public interface IEnemyFactory {
    Enemy CreateEnemy();
}

public class GoblinFactory : IEnemyFactory {
    public Enemy CreateEnemy() => new Goblin();
}

public class OrcFactory : IEnemyFactory {
    public Enemy CreateEnemy() => new Orc();
}

public class EnemySpawner {
    private Dictionary<EnemyType, IEnemyFactory> factories;
    
    public Enemy SpawnEnemy(EnemyType type) {
        return factories[type].CreateEnemy();
    }
}

L — Liskov Substitution Principle (LSP)

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

// ❌ Нарушает LSP — Flying Bird не может летать
public class Bird {
    public virtual void Fly() { }
}

public class Penguin : Bird {
    public override void Fly() {
        throw new NotImplementedException("Пингвин не летает!");
    }
}

// Это нарушение — код, работающий с Bird, сломается на Penguin

// ✅ Правильно — правильная иерархия
public class Bird { }
public class FlyingBird : Bird {
    public virtual void Fly() { }
}
public class Penguin : Bird { }
public class Eagle : FlyingBird {
    public override void Fly() { /* реализация */ }
}

I — Interface Segregation Principle (ISP)

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

// ❌ Нарушает ISP — придётся реализовать ненужные методы
public interface IGameEntity {
    void TakeDamage(int amount);
    void Attack();
    void Fly();
    void Swim();
}

public class Player : IGameEntity {
    public void Fly() { } // Не нужно, но вынужден реализовать
    public void Swim() { } // Не нужно, но вынужден реализовать
}

// ✅ Правильно — специальные интерфейсы
public interface IDamageable {
    void TakeDamage(int amount);
}

public interface ICombatant {
    void Attack();
}

public interface IAirborne {
    void Fly();
}

public class Player : IDamageable, ICombatant {
    // Реализуем только нужные интерфейсы
}

public class Dragon : IDamageable, ICombatant, IAirborne {
    // Реализуем всё
}

D — Dependency Inversion Principle (DIP)

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

// ❌ Нарушает DIP — прямая зависимость от конкретного класса
public class Player {
    private SqliteDatabase database = new SqliteDatabase();
    
    public void SaveProgress() {
        database.Save(this);
    }
}

// ❌ Сложно тестировать, привязано к SqliteDatabase

// ✅ Правильно — зависимость от интерфейса
public interface IDatabase {
    void Save(Player player);
}

public class Player {
    private IDatabase database;
    
    public Player(IDatabase database) {
        this.database = database;
    }
    
    public void SaveProgress() {
        database.Save(this);
    }
}

// Теперь легко тестировать
public class MockDatabase : IDatabase {
    public void Save(Player player) { /* mock */ }
}

// И легко менять реализацию
public class SqliteDatabase : IDatabase {
    public void Save(Player player) { /* real save */ }
}

Применение в Unity

Для эффективного использования SOLID в Unity:

  1. Используй Dependency Injection — PassBy через constructor или ServiceLocator
  2. Создавай интерфейсы рано — не жди, пока понадобятся две реализации
  3. Избегай монолитных MonoBehaviour — разделяй на smaller, focused компоненты
  4. Тестируй — SOLID облегчает unit testing
  5. Не переусложняй — SOLID—это инструмент, а не закон. Для простого скрипта часто не нужен весь SOLID

Эти принципы делают код более гибким и поддерживаемым в долгосрочной перспективе.