
Принципы SOLID — это набор пяти основных принципов, которые помогают разработчикам создавать более понятный, гибкий и поддерживаемый код. Так же знание этих принципов довольно часто спрашивают на собеседованиях. Давайте рассмотрим каждый из этих принципов с примерами нарушения и соблюдения на языке Swift:
Принцип единственной ответственности (Single Responsibility Principle, SRP): Этот принцип гласит, что у каждого класса или структуры должна быть только одна задача. Например, если у вас есть класс для обработки данных из сети, его не следует использовать для отображения данных на экране.
Нарушение принципа SPR:
// NetworkManager только выполняет сетевые запросы class NetworkManager { func fetchData(url: URL) { // Запрос к API } func updateUI() { // обновляет пользовательский интерфейс } }
В этом примере класс NetworkManager нарушает принцип SPR, потому что он и получает данные из сети, и обновляет UI.
Соблюдение принципа SPR:
// NetworkManager только выполняет сетевые запросы class NetworkManager { func fetchData(url: URL) { // Запрос к API } } // ViewController управляет отображением данных class ViewController: UIViewController { let networkManager = NetworkManager() func updateUI() { let url = URL(string: "https://api.example.com")! networkManager.fetchData(url: url) // Обновить интерфейс пользователя с данными } }
Принцип открытости/закрытости (Open-Closed Principle, OCP): Классы и функции должны быть открыты для расширения, но закрыты для изменений. Это означает, что вы должны быть в состоянии добавить новую функциональность без изменения существующего кода. Это можно сделать с помощью протоколов и расширений в Swift.
Нарушение принципа OCP:
class Animal { let name: String init(name: String) { self.name = name } func makeSound() { if name == "Dog" { print("Woof") } else if name == "Cat" { print("Meow") } } }
Здесь класс Animal не закрыт для модификации, потому что если мы захотим добавить новое животное, нам придется изменить метод makeSound.
Соблюдение принципа OCP:
protocol Animal { func makeSound() } class Dog: Animal { func makeSound() { print("Woof") } } class Cat: Animal { func makeSound() { print("Meow") } }
Теперь каждый класс подписанный под протокол Animal может иметь свою собственную реализацию makeSound, и протокол Animal закрыт для модификаций.
Принцип подстановки Барбары Лисков (Liskov Substitution Principle, LSP): Это означает, что если у вас есть класс B, который является подклассом класса A, вы должны иметь возможность использовать B везде, где используется A, без изменения поведения программы.
Нарушение принципа LSP:
class Bird { func fly() { // Реализация полета } } class Penguin: Bird { override func fly() { fatalError("Penguins can't fly!") } } let myBird: Bird = Penguin() myBird.fly() // Приведет к ошибке во время выполнения
В этом примере Penguin нарушает LSP, потому что он не может летать, в то время как класс Bird предполагает, что все птицы могут летать.
Соблюдение принципа LSP:
class Bird { func move() { print("The bird is flying") } } class Penguin: Bird { override func move() { print("The penguin is sliding") } } let myBird: Bird = Penguin() myBird.move()
Теперь каждый подкласс Bird может иметь свою собственную реализацию move, не нарушая поведение базового класса.
Принцип разделения интерфейса (Interface Segregation Principle, ISP): Это означает, что классы не должны зависеть от методов, которые они не используют. В Swift это можно сделать с помощью протоколов.
Нарушение принципа ISP:
protocol Worker { func work() func eat() } class Robot: Worker { func work() { // работает } func eat() { fatalError("Robots can't eat") } }
Здесь Robot нарушает ISP, потому что он вынужден реализовать функцию eat, которую он не может использовать.
Соблюдение принципа ISP:
protocol Worker { func work() } protocol Eater { func eat() } class Robot: Worker { func work() { // работает } }
Теперь Robot реализует только функции, которые он действительно может использовать.
Принцип инверсии зависимостей (Dependency Inversion Principle, DIP): Это означает, что классы верхнего уровня не должны зависеть от классов нижнего уровня. Оба они должны зависеть от абстракций.
Нарушение принципа DIP:
class LightBulb { func turnOn() { // включает свет } } class Switch { let bulb: LightBulb init(bulb: LightBulb) { self.bulb = bulb } func toggle() { bulb.turnOn() } }
Здесь класс Switch напрямую зависит от конкретного класса LightBulb, что нарушает DIP.
Соблюдение принципа DIP:
protocol Switchable { func turnOn() } class LightBulb: Switchable { func turnOn() { // включает свет } } class Switch { let device: Switchable init(device: Switchable) { self.device = device } func toggle() { device.turnOn() } }
Теперь класс Switch зависит от абстракции, а не от конкретного класса, что соблюдает DIP.
