9 принципов проектирования для написания чистого кода (с примерами Swift)

Чистый код — это не просто работающий код. Это код, который легко читать, тестировать и поддерживать. Ниже представлены 9 ключевых принципов проектирования, которые помогают писать чистый и поддерживаемый код. Примеры приведены на языке Swift.

1 DRY — Don’t Repeat Yourself (Не повторяй себя)

Суть: Избегай дублирования логики. Централизуй повторяющийся код, чтобы облегчить поддержку.

❌ Плохо:

let area1 = width1 * height1 let area2 = width2 * height2 let area3 = width3 * height3

✅ Хорошо:

func calculateArea(width: Double, height: Double) -> Double { width * height } let areas = [ calculateArea(width: 10, height: 5), calculateArea(width: 8, height: 4), calculateArea(width: 12, height: 6) ]

2 KISS — Keep It Simple, Stupid (Делай проще)

Суть: Не переусложняй решения. Избегай избыточных абстракций и ненужных уровней логики.

❌ Плохо:

protocol Shape { func calculateArea() -> Double } class Rectangle: Shape { var width: Double var height: Double init(width: Double, height: Double) { self.width = width self.height = height } func calculateArea() -> Double { return width * height } }

✅ Хорошо:

func rectangleArea(width: Double, height: Double) -> Double { width * height }

💡 Делай просто, пока нет реальной необходимости усложнять.

3 YAGNI — You Aren’t Gonna Need It (Тебе это не понадобится)

Суть: Не реализуй функционал «на будущее». Пиши только то, что нужно сейчас.

❌ Плохо:

class NetworkManager { func fetchData() { // TODO: добавить кэш, retry policy, логирование, метрики... } }

✅ Хорошо:

class NetworkManager { func fetchData() { // Минимально достаточная логика print("Fetching data...") } }

💡 Код, написанный «на всякий случай», почти всегда усложняет проект.

4 LOD — Law of Demeter (Закон Деметры)

Суть: Общайся только с ближайшими «соседями». Избегай длинных цепочек вызовов.

❌ Плохо:

let cityName = user.profile.address.city.name

✅ Хорошо:

let cityName = user.cityName

💡 Лучше инкапсулировать доступ к внутренним свойствам в отдельные методы.

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

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

❌ Плохо:

class UserManager { func saveUser() { /* сохранение */ } func sendWelcomeEmail() { /* отправка письма */ } }

✅ Хорошо:

class UserStorage { func saveUser() { /* сохранение */ } } class UserNotifier { func sendWelcomeEmail() { /* отправка письма */ } }

💡 Делегируй разные обязанности разным компонентам.

6 OCP — Open/Closed Principle (Открыт для расширения, закрыт для модификации)

Суть: Класс должен быть открыт для расширения, но закрыт для изменений.

❌ Плохо:

class PaymentProcessor { func process(type: String) { if type == "Visa" { /* ... */ } else if type == "MasterCard" { /* ... */ } } }

✅ Хорошо:

protocol Payment { func process() } class VisaPayment: Payment { func process() { print("Processing Visa...") } } class MasterCardPayment: Payment { func process() { print("Processing MasterCard...") } } class PaymentProcessor { func process(payment: Payment) { payment.process() } }

💡 Добавление нового типа оплаты не требует изменения PaymentProcessor.

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

Суть: Подкласс должен заменять родительский класс без нарушения логики программы.

❌ Плохо:

class Bird { func fly() {} } class Penguin: Bird { override func fly() { fatalError("Penguins can’t fly!") } }

✅ Хорошо:

class Bird {} class FlyingBird: Bird { func fly() {} } class Penguin: Bird {}

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

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

Суть: Лучше создавать маленькие, специализированные интерфейсы, чем один большой и универсальный.

❌ Плохо:

protocol Worker { func code() func test() func design() }

✅ Хорошо:

protocol Coder { func code() } protocol Tester { func test() }

💡 Пусть объекты реализуют только те методы, которые им действительно нужны.

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

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

❌ Плохо:

class APIService { func fetch() { print("Fetching...") } } class ViewModel { let service = APIService() }

✅ Хорошо:

protocol APIServiceProtocol { func fetch() } class APIService: APIServiceProtocol { func fetch() { print("Fetching...") } } class ViewModel { private let service: APIServiceProtocol init(service: APIServiceProtocol) { self.service = service } }

💡 Это упрощает тестирование, потому что можно подставить мок-реализацию.

🧠 Заключение

Соблюдение этих принципов не делает код автоматически идеальным — но делает его понятным, предсказуемым и безопасным для изменений. Хороший код — это не только про работу программы, но и про удобство чтения для следующего разработчика (или для вас самого через 3 месяца).

1
Начать дискуссию