solid с примерами на golang

# SOLID принципы с примерами на Go SOLID - это набор принципов объектно-ориентированного проектирования, которые помогают создавать гибкое и поддерживаемое программное обеспечение. Давайте рассмотрим каждый из них с примерами на Go. ## 1. Принцип единственной ответственности (Single Responsibility Principle - SRP) Класс должен иметь только одну причину для изменения, то есть только одну ответственность. **Пример нарушения SRP:** ```go type Report struct { content string } // Нарушение SRP: Report отвечает и за формирование отчета, и за его сохранение func (r *Report) Generate(content string) { r.content = content } func (r *Report) SaveToFile(filename string) error { return os.WriteFile(filename, []byte(r.content), 0644) } ``` **Исправленный вариант:** ```go type Report struct { content string } func (r *Report) Generate(content string) { r.content = content } type ReportSaver struct{} func (rs *ReportSaver) SaveToFile(report *Report, filename string) error { return os.WriteFile(filename, []byte(report.content), 0644) } ``` ## 2. Принцип открытости/закрытости (Open/Closed Principle - OCP) Программные сущности должны быть открыты для расширения, но закрыты для модификации. **Пример нарушения OCP:** ```go type Circle struct { Radius float64 } type Square struct { Side float64 } func CalculateArea(shape interface{}) float64 { switch s := shape.(type) { case Circle: return math.Pi * s.Radius * s.Radius case Square: return s.Side * s.Side default: return 0 } } // При добавлении новой фигуры нужно модифицировать функцию ``` **Исправленный вариант:** ```go type Shape interface { Area() float64 } type Circle struct { Radius float64 } func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius } type Square struct { Side float64 } func (s Square) Area() float64 { return s.Side * s.Side } func CalculateTotalArea(shapes []Shape) float64 { total := 0.0 for _, shape := range shapes { total += shape.Area() } return total } ``` ## 3. Принцип подстановки Барбары Лисков (Liskov Substitution Principle - LSP) Объекты в программе должны быть заменяемыми на экземпляры их подтипов без изменения правильности программы. **Пример нарушения LSP:** ```go type Bird interface { Fly() string } type Duck struct{} func (d Duck) Fly() string { return "Duck flying" } type Ostrich struct{} func (o Ostrich) Fly() string { return "Ostrich can't fly" // Нарушение LSP: страус не может летать } ``` **Исправленный вариант:** ```go type Bird interface { Move() string } type FlyingBird interface { Bird Fly() string } type Duck struct{} func (d Duck) Move() string { return d.Fly() } func (d Duck) Fly() string { return "Duck flying" } type Ostrich struct{} func (o Ostrich) Move() string { return "Ostrich running" } ``` ## 4. Принцип разделения интерфейса (Interface Segregation Principle - ISP) Клиенты не должны зависеть от интерфейсов, которые они не используют. **Пример нарушения ISP:** ```go type Worker interface { Work() Eat() Sleep() } type HumanWorker struct{} func (h HumanWorker) Work() { /*...*/ } func (h HumanWorker) Eat() { /*...*/ } func (h HumanWorker) Sleep() { /*...*/ } type RobotWorker struct{} func (r RobotWorker) Work() { /*...*/ } func (r RobotWorker) Eat() {} // Пустая реализация - нарушение ISP func (r RobotWorker) Sleep() {} // Пустая реализация - нарушение ISP ``` **Исправленный вариант:** ```go type Worker interface { Work() } type Eater interface { Eat() } type Sleeper interface { Sleep() } type HumanWorker struct{} func (h HumanWorker) Work() { /*...*/ } func (h HumanWorker) Eat() { /*...*/ } func (h HumanWorker) Sleep() { /*...*/ } type RobotWorker struct{} func (r RobotWorker) Work() { /*...*/ } ``` ## 5. Принцип инверсии зависимостей (Dependency Inversion Principle - DIP) Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций. **Пример нарушения DIP:** ```go type MySQLDatabase struct{} func (db MySQLDatabase) Query(query string) []string { // Реализация запроса к MySQL return []string{"result1", "result2"} } type ReportGenerator struct { db MySQLDatabase // Зависимость от конкретной реализации } func (rg ReportGenerator) GenerateReport() { results := rg.db.Query("SELECT * FROM reports") // Генерация отчета } ``` **Исправленный вариант:** ```go type Database interface { Query(query string) []string } type MySQLDatabase struct{} func (db MySQLDatabase) Query(query string) []string { // Реализация запроса к MySQL return []string{"result1", "result2"} } type ReportGenerator struct { db Database // Зависимость от абстракции } func (rg ReportGenerator) GenerateReport() { results := rg.db.Query("SELECT * FROM reports") // Генерация отчета } ``` Эти принципы помогают создавать более гибкий, расширяемый и поддерживаемый код на Go, даже несмотря на то, что Go не является чисто объектно-ориентированным языком.