Приведи пример нарушения принципа SOLID в коде

Ответ

Нарушение Принципа единственной ответственности (Single Responsibility Principle - SRP): Класс ReportGenerator отвечает и за формирование данных отчета, и за их форматирование в HTML, и за отправку по email.

class ReportGenerator {
    func fetchData() -> Data { /* ... */ }
    func analyzeData(_ data: Data) -> AnalysisResult { /* ... */ }
    func createHTMLReport(from result: AnalysisResult) -> String { /* ... */ }
    func sendEmail(with html: String, to recipient: String) { /* ... */ }
}
// Нарушение: класс имеет как минимум три причины для изменения:
// 1. Изменение логики получения данных.
// 2. Изменение формата отчета (PDF, CSV).
// 3. Изменение способа доставки (сохранение в файл, push-уведомление).

Исправленная архитектура (соответствует SRP):

class DataFetcher { func fetch() -> Data { /* ... */ } }
class DataAnalyzer { func analyze(_ data: Data) -> AnalysisResult { /* ... */ } }
class HTMLReportBuilder { func build(from result: AnalysisResult) -> String { /* ... */ } }
class EmailSender { func send(content: String, to recipient: String) { /* ... */ } }
// Каждый класс имеет одну, четко определенную ответственность.

Нарушение Принципа открытости/закрытости (Open/Closed Principle - OCP): Код не закрыт для модификаций при добавлении новой функциональности.

class AreaCalculator {
    func area(of shape: Any) -> Double {
        if let circle = shape as? Circle {
            return Double.pi * pow(circle.radius, 2)
        } else if let square = shape as? Square {
            return pow(square.side, 2)
        }
        // Добавление нового типа фигуры (Triangle) требует изменения этого метода.
        return 0
    }
}

Исправление с помощью протокола (соответствует OCP):

protocol Shape {
    var area: Double { get }
}

struct Circle: Shape {
    let radius: Double
    var area: Double { Double.pi * radius * radius }
}
struct Square: Shape {
    let side: Double
    var area: Double { side * side }
}
// Чтобы добавить Triangle, нужно создать новый тип, соответствующий протоколу Shape.
// Класс AreaCalculator изменять не требуется.
class AreaCalculator {
    func totalArea(of shapes: [Shape]) -> Double {
        return shapes.reduce(0) { $0 + $1.area }
    }
}

Ответ 18+ 🔞

О, слушай, смотри, вот это классика, блядь! Прямо как в том анекдоте про мужика, который и печку топит, и обед готовит, и на балалайке играет, а потом спрашивает: «Чё-то я устал, нахуй». Вот и тут так же.

Смотри, есть у нас этот ReportGenerator, сука. А он у нас, блядь, универсальный солдат, мастер на все руки! Он и данные тащит (fetchData), и их анализирует (analyzeData), и в HTML превращает (createHTMLReport), и ещё почту отправляет (sendEmail). Ну ёпта, чувак, ты чё, заебался уже? Это ж пиздец как нарушение! Это как если бы один человек в ресторане был и поваром, и официантом, и кассиром, и мойщиком посуды. Он бы сдох нахуй через час, а код — через неделю поддержки.

Правило одно, блядь (SRP): Один класс — одна работа. Одна причина, чтобы его менять. А тут у него причин — овердохуища! Захотел отчёт в PDF — меняй класс. Захотел слать не на почту, а в телегу — опять меняй этот же класс. Это ж пиздец, мартышлюшка какая-то, а не архитектура!

Исправляем, блядь, разводим этих работничков по отдельным кабинетам:

  • DataFetcher — сидит, данные тащит. Больше него нихуя не волнует.
  • DataAnalyzer — получил данные, нихуя не думает, откуда они, просто анализирует.
  • HTMLReportBuilder — его дело — из результата анализа сделать красивый HTML. Про почту он даже не слышал.
  • EmailSender — его работа — взять что угодно (хоть HTML, хоть текст, хоть картинку с котиком) и отправить. Откуда это что-то взялось — ему похуй.

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


А вот это, блядь, второй номер нашей программы — принцип «открыт для расширения, но закрыт для изменений» (OCP). Звучит как заклинание, но смысл простой, ёпта.

Смотри, вот у нас калькулятор площади. И он, сука, как тот менеджер-самодур: «А, круг? Значит, так считаем. А, квадрат? Ну тогда вот так. Чего? Треугольник? А я про треугольники нихуя не знаю, иди нахуй, допиши мне в код!».

class AreaCalculator {
    func area(of shape: Any) -> Double {
        if let circle = shape as? Circle { // Проверяем круг
            return Double.pi * pow(circle.radius, 2)
        } else if let square = shape as? Square { // Проверяем квадрат
            return pow(square.side, 2)
        }
        // Бля, а если треугольник? Опять лезть сюда и этот if дописывать? Пиздец.
        return 0
    }
}

Каждый раз, когда нам в жопу прилетает новая фигура, нам надо лезть в этот метод и пихать туда новый if. Это и есть «открыт для изменений», но не в том смысле, блядь! Это как постоянно перестраивать фундамент дома, когда хочешь добавить балкон.

А как надо? Надо сделать так, чтобы сам калькулятор был тупой как пробка и нихуя не знал про конкретные фигуры. Его дело — спросить: «Эй, фигура, а площадь у тебя какая?». А уж фигура сама пусть считает, как хочет.

Вот для этого протокол Shape — он как контракт, блядь. Подписался — будь добр, свойство area предоставь.

protocol Shape {
    var area: Double { get } // Всем фигурам: ребята, у кого площадь есть — проходите.
}

struct Circle: Shape {
    let radius: Double
    var area: Double { Double.pi * radius * radius } // Круг считает по-своему.
}
struct Square: Shape {
    let side: Double
    var area: Double { side * side } // Квадрат — по-своему.
}

class AreaCalculator {
    func totalArea(of shapes: [Shape]) -> Double {
        return shapes.reduce(0) { $0 + $1.area } // А я, калькулятор, тупо всё складываю. Мне похуй, кто как считал.
    }
}

Теперь, сука, хоть трапецию, хоть пятиугольник, хоть хуй в пальто в виде фигуры добавляй — просто создай новый тип, подпиши протокол Shape и реализуй area. Калькулятор AreaCalculator трогать не надо! Он закрыт для изменений, но открыт для того, чтобы работать с любыми новыми фигурами. Вот это и есть магия, блядь, а не костыли с if.