SOLID для начинающих Unity-разработчиков: простыми словами и с примерами из жизни

от автора

Если ты только начал программировать на Unity C#, то наверняка слышал про SOLID.
На каждом собесе спрашивают: «Что такое SOLID? Зачем он нужен? Приведи пример». Но когда ты новичок, кажется, что это что-то сложное и непонятное. Давай разберёмся вместе — просто, по-человечески и с примерами из жизни.


Что такое SOLID?

Вот стандартное описание которое пишут всегда:

SOLID — это пять простых правил, которые помогают писать понятный и удобный код. Если следовать этим правилам, твой проект будет проще поддерживать, дорабатывать и не будет превращаться в кашу.

SOLID — это аббревиатура:

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

  • O — Open/Closed Principle (Принцип открытости/закрытости)

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

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

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


Но зачем все таки нужен SOLID. Чем он помогает. И что вообще это такое?

Когда ты только начинаешь программировать, кажется, что можно просто писать код, и всё будет работать. Но со временем проект растёт, появляются новые фичи, баги, задачи.
Если не соблюдать SOLID, код становится запутанным, его сложно менять, а ошибки появляются всё чаще.

Представь, что ты строишь дом. Сначала всё просто — фундамент, стены, крыша. Но если не следовать правилам строительства, дом быстро начнёт разваливаться: где-то трещина, где-то дверь не закрывается, а если захочешь пристроить балкон — придётся ломать полдома. Так и с кодом: без принципов всё держится на честном слове, и любое изменение может всё сломать.

SOLID — это как инструкция по строительству: если ей следовать, твой проект будет расти без хаоса. Ты сможешь:

  • Легко читать и понимать свой код (даже если вернёшься к нему через месяц или полгода)

  • Быстро добавлять новые фичи, не боясь, что что-то сломается

  • Легко находить и исправлять ошибки

  • Не бояться изменений — твой код будет гибким и устойчивым

  • Работать в команде: другим будет проще разобраться в твоём проекте

Пример из Unity:
Если ты пишешь игру и не следуешь SOLID, то через пару месяцев твой скрипт PlayerController может вырасти до 1000 строк, где намешано всё: движение, стрельба, здоровье, управление UI и даже музыка. Исправить баг или добавить новую механику становится мучением. А если ты разделяешь код по принципам SOLID — каждый скрипт отвечает только за своё, и менять или расширять игру становится легко и приятно.


Примеры из жизни и кода для каждого принципа:

S — Single Responsibility Principle

Один класс — одна ответственность
Один человек — одна задача.

Пример:
В кафе есть бариста, который варит кофе, и повар, который готовит еду. Если бариста начнёт готовить бургеры, а повар — варить кофе, будет бардак. Пусть каждый делает своё дело.

Пример в коде:
// Класс только для движения игрока public class PlayerMover : MonoBehaviour {     public void Move(Vector3 direction) {         transform.Translate(direction);     } } 

Что происходит в коде:
Класс PlayerMover отвечает только за движение игрока и не занимается ничем другим. Это и есть принцип одной ответственности.


O — Open/Closed Principle

Открыт для расширения, закрыт для изменения
Добавляй новое, не ломая старое.

Пример:
В телефоне можно поставить новое приложение, не перепрошивая всю систему. Так и в коде: добавляешь новую фичу — не трогаешь рабочие части.

Хочешь добавить новый тип врага — не переписывай весь GameManager, а просто добавь новый класс врага.

Пример в коде:
// Базовый класс врага public abstract class Enemy : MonoBehaviour {     public abstract void Attack(); } // Добавляем новый тип врага без изменения существующего кода public class Zombie : Enemy {     public override void Attack() { /* ... */ } } public class Robot : Enemy {     public override void Attack() { /* ... */ } } 

Что происходит в коде:
Ты можешь добавлять новых врагов (Zombie, Robot), не меняя код базового класса Enemy. Старый код не ломается, а расширяется.

Почему используется abstract class?

Абстрактный класс Enemy — это как общий шаблон для всех врагов. В нём описано, что у любого врага должна быть атака (метод Attack), но как именно атаковать — решает каждый конкретный враг (Zombie, Robot). Благодаря этому ты можешь создавать новых врагов, просто наследуя этот шаблон, и не трогать уже написанный код. Это и есть суть принципа: расширяем, не изменяя старое.


L — Liskov Substitution Principle

Замена без сюрпризов.

Пример:
Ты арендуешь машину. Какая бы марка ни была — главное, чтобы она ехала и тормозила. Так и в коде: если функция ждёт «животное», можно подставить «кошку» или «собаку», и всё будет работать.

Пример в коде:
// Любой Enemy можно подставить void DamageEnemy(Enemy enemy) {     enemy.Attack(); } 

Что происходит в коде:
Функция DamageEnemy работает с любым наследником Enemy — не важно, зомби это или робот. Всё будет работать одинаково.


I — Interface Segregation Principle

Лучше несколько маленьких меню, чем одно огромное.

Пример:
В ресторане есть отдельное меню для напитков и отдельное для десертов. Никто не хочет искать среди 50 страниц мороженое.

Пример в коде:
// Маленькие интерфейсы public interface IShoot {     void Shoot(); } public interface IReload {     void Reload(); }  // Пистолет реализует оба интерфейса public class Pistol : IShoot, IReload {     public void Shoot() { /* стреляет */ }     public void Reload() { /* перезаряжается */ } }  // Граната реализует только стрельбу public class Grenade : IShoot {     public void Shoot() { /* взрывается */ }     // Нет метода Reload } 

Что происходит в коде:
Вместо одного большого интерфейса для оружия, есть отдельные интерфейсы для стрельбы и перезарядки. Класс может реализовать только то, что ему нужно.

Что такое interface?

Interface — это как договор или инструкция, в которой написано, какие методы должны быть у класса. Но сам interface не содержит реализацию — он только говорит: «Если ты реализуешь этот interface, у тебя обязательно должен быть такой-то метод». Например, если класс реализует IShoot, значит, в нём обязательно будет метод Shoot(). Это помогает делать код более гибким и понятным.


D — Dependency Inversion Principle

Зависеть от абстракций, а не от деталей.

Пример:
Ты вызываешь такси через приложение. Тебе всё равно, какая машина приедет — главное, чтобы она довезла до точки Б.

Пример в коде:
// Работаем с абстракцией public class WeaponUser {     private IShoot weapon;     public WeaponUser(IShoot weapon) {         this.weapon = weapon;     }     public void UseWeapon() {         weapon.Shoot();     } } 

Что происходит в коде:
Класс WeaponUser работает с любым оружием, которое реализует интерфейс IShoot. Можно легко подменить пистолет на лазер — код не изменится.


Итог

SOLID — это пять простых принципов, которые помогают не превращать твой код в кашу.
Каждый принцип — это напоминание: не усложняй, не повторяйся, не делай лишнего, не мешай всё в одну кучу и не делай код зависимым.

А так же рекомендую изучить принципы KISS, DRY, YAGNI и BDUF — они отлично дополняют SOLID!


ссылка на оригинал статьи https://habr.com/ru/articles/940620/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *