Ответ
Нарушение Принципа единственной ответственности (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.