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 не является чисто объектно-ориентированным языком.