【Goで学ぶコンピューターサイエンス】設計と開発の基本原則

## **1. 高凝集・低結合**
### **概要**
- **高凝集**: クラスやモジュールの内部では、似たような機能をまとめ、一貫した目的を持たせます
- **低結合**: 他のクラスやモジュールとの依存関係を減らします

### **メリット**
- 修正が容易になり、他のモジュールへの影響を最小限に抑えられます
- テストが容易になります

### **例**
**高凝集・低結合の悪い例**  
1つのクラスに異なる機能を詰め込んでいる
```go
type BadExample struct{}

func (b *BadExample) SaveUserToDB(user string) {
    // データベースへの保存
}

func (b *BadExample) SendEmail(email string) {
    // メール送信
}
```

**改善例**  
異なる機能を異なるクラスに分割
```go
type UserRepository struct{}

func (r *UserRepository) SaveUserToDB(user string) {
    // データベースへの保存
}

type EmailService struct{}

func (s *EmailService) SendEmail(email string) {
    // メール送信
}
```

## **2. KISS (Keep It Simple, Stupid)**
### **概要**
- シンプルで分かりやすいコードを書くことを目指します
- 不要な複雑さを避け、誰でも理解できる設計にします

### **メリット**
- 保守性が向上し、バグを減らせます

### **例**
**悪い例: 複雑すぎるロジック**  
```go
func Calculate(value int) int {
    if value < 10 {
        return value * 2
    } else if value >= 10 && value < 20 {
        return value * 3
    } else {
        return value * 4
    }
}
```

**改善例: シンプルで直感的なロジック**
```go
func Calculate(value int) int {
    multiplier := 2
    if value >= 10 {
        multiplier = 3
    }
    if value >= 20 {
        multiplier = 4
    }
    return value * multiplier
}
```

## **3. DRY (Don't Repeat Yourself)**
### **概要**
- 同じコードやロジックを複数箇所で繰り返さないようにします
- 再利用可能なコードに抽象化します

### **メリット**
- 一箇所を修正すれば全体に反映されるため、保守が容易になります

### **例**
**悪い例: 重複したコード**
```go
func SendWelcomeEmail(email string) {
    fmt.Printf("Sending email to: %s\n", email)
}

func SendPasswordResetEmail(email string) {
    fmt.Printf("Sending email to: %s\n", email)
}
```

**改善例: 共通の処理を抽出**
```go
func SendEmail(email string, message string) {
    fmt.Printf("Sending email to: %s with message: %s\n", email, message)
}

func SendWelcomeEmail(email string) {
    SendEmail(email, "Welcome!")
}

func SendPasswordResetEmail(email string) {
    SendEmail(email, "Reset your password")
}
```

## **4. YAGNI (You Aren't Gonna Need It)**
### **概要**
- 必要になるか分からない機能を予め実装しない
- 必要性が明確になるまで作業を延期します

### **メリット**
- 不必要なコードを減らし、開発コストを削減できます

### **例**
**悪い例: 必要になるか分からない機能の実装**
```go
type PaymentProcessor struct{}

func (p *PaymentProcessor) ProcessCreditCard() {
    // 実装
}

func (p *PaymentProcessor) ProcessCryptocurrency() {
    // 実装（必要になるか不明だが追加）
}
```

**改善例: 必要になったときに追加**
```go
type PaymentProcessor struct{}

func (p *PaymentProcessor) ProcessCreditCard() {
    // 実装
}
```

## **5. SOLID原則**

### **S: 単一責任の原則 (Single Responsibility Principle)**
- 1つのクラスが1つの責任（目的）のみを持つべき
- **例**: `User`クラスはユーザー管理のみを担当し、メール送信など他の責任を持たない

**悪い例: 複数の責任を持つ構造体**
```go
type User struct {
    Name  string
    Email string
}

func (u *User) SaveToDB() {
    fmt.Println("Saving user to database")
}

func (u *User) SendWelcomeEmail() {
    fmt.Println("Sending welcome email")
}
```

**改善例: 責任を分離**
```go
type User struct {
    Name  string
    Email string
}

type UserRepository struct{}

func (r *UserRepository) SaveToDB(user User) {
    fmt.Println("Saving user to database:", user.Name)
}

type EmailService struct{}

func (e *EmailService) SendWelcomeEmail(user User) {
    fmt.Println("Sending welcome email to:", user.Email)
}

// 利用例
func main() {
    user := User{Name: "John", Email: "john@example.com"}
    repo := UserRepository{}
    emailService := EmailService{}

    repo.SaveToDB(user)
    emailService.SendWelcomeEmail(user)
}
```

### **O: 開放・閉鎖の原則 (Open/Closed Principle)**
- コードは拡張に対して開かれ、変更に対して閉じているべき
- **例**: 新しい支払い方法を追加する場合でも、既存のコードを変更せずに対応できる設計にする

**悪い例: 支払い方法を追加する際に既存コードを変更**
```go
type PaymentProcessor struct{}

func (p *PaymentProcessor) ProcessPayment(method string) {
    if method == "credit_card" {
        fmt.Println("Processing credit card payment")
    } else if method == "paypal" {
        fmt.Println("Processing PayPal payment")
    } else {
        fmt.Println("Unknown payment method")
    }
}
```

**改善例: 新しい支払い方法を追加しても既存コードを変更しない**
```go
type PaymentMethod interface {
    Pay()
}

type CreditCardPayment struct{}

func (c CreditCardPayment) Pay() {
    fmt.Println("Processing credit card payment")
}

type PayPalPayment struct{}

func (p PayPalPayment) Pay() {
    fmt.Println("Processing PayPal payment")
}

func ProcessPayment(method PaymentMethod) {
    method.Pay()
}

// 利用例
func main() {
    creditCard := CreditCardPayment{}
    paypal := PayPalPayment{}

    ProcessPayment(creditCard)
    ProcessPayment(paypal)
}
```

### **L: リスコフ置換の原則 (Liskov Substitution Principle)**
- サブクラスはスーパークラスの機能を壊さずに置き換え可能であるべき
- **例**: `Rectangle`と`Square`を使うとき、`Square`が`Rectangle`の機能を損なわない

**悪い例: サブタイプがスーパークラスの期待を満たさない**
```go
type Rectangle struct {
    Width  int
    Height int
}

func (r Rectangle) Area() int {
    return r.Width * r.Height
}

type Square struct {
    Side int
}

func (s Square) Area() int {
    // スーパークラスの期待を破壊
    return 4 * s.Side
}
```

**改善例: サブタイプがスーパークラスとして動作**
```go
type Shape interface {
    Area() int
}

type Rectangle struct {
    Width  int
    Height int
}

func (r Rectangle) Area() int {
    return r.Width * r.Height
}

type Square struct {
    Side int
}

func (s Square) Area() int {
    return s.Side * s.Side
}

// 利用例
func main() {
    var shapes []Shape
    shapes = append(shapes, Rectangle{Width: 10, Height: 5})
    shapes = append(shapes, Square{Side: 4})

    for _, shape := range shapes {
        fmt.Println("Area:", shape.Area())
    }
}
```

### **I: インターフェース分離の原則 (Interface Segregation Principle)**
- 1つの大きなインターフェースを小さなインターフェースに分割する
- **例**: 不要な機能を含まないインターフェースを提供

**悪い例: すべての機能を1つのインターフェースにまとめる**
```go
type Worker interface {
    Work()
    Eat()
}

type Robot struct{}

func (r Robot) Work() {
    fmt.Println("Robot is working")
}

// RobotはEatメソッドを必要としないが、実装を強いられる
func (r Robot) Eat() {
    fmt.Println("Robot does not eat")
}
```

**改善例: インターフェースを分割**
```go
type Worker interface {
    Work()
}

type Eater interface {
    Eat()
}

type Human struct{}

func (h Human) Work() {
    fmt.Println("Human is working")
}

func (h Human) Eat() {
    fmt.Println("Human is eating")
}

type Robot struct{}

func (r Robot) Work() {
    fmt.Println("Robot is working")
}

// 利用例
func main() {
    var worker Worker

    worker = Human{}
    worker.Work()

    worker = Robot{}
    worker.Work()
}
```

### **D: 依存性逆転の原則 (Dependency Inversion Principle)**
- 高レベルモジュールは低レベルモジュールに依存してはならず、抽象化に依存すべき

**悪い例: 高レベルモジュールが低レベルモジュールに依存**
```go
type MySQLDatabase struct{}

func (db MySQLDatabase) Connect() {
    fmt.Println("Connecting to MySQL database")
}

type Application struct {
    Database MySQLDatabase
}

func (app Application) Start() {
    app.Database.Connect()
}
```

**改善例: 抽象化を使用して依存を逆転**
```go
type Database interface {
    Connect()
}

type MySQLDatabase struct{}

func (db MySQLDatabase) Connect() {
    fmt.Println("Connecting to MySQL database")
}

type PostgreSQLDatabase struct{}

func (db PostgreSQLDatabase) Connect() {
    fmt.Println("Connecting to PostgreSQL database")
}

type Application struct {
    Database Database
}

func (app Application) Start() {
    app.Database.Connect()
}

// 利用例
func main() {
    mysql := MySQLDatabase{}
    postgres := PostgreSQLDatabase{}

    app := Application{Database: mysql}
    app.Start()

    app = Application{Database: postgres}
    app.Start()
}
```