> For the complete documentation index, see [llms.txt](https://bagus-cahyono.gitbook.io/programming-notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bagus-cahyono.gitbook.io/programming-notes/programming/solid.md).

# SOLID

is a set of five design principles introduced by Robert C. Martin (Uncle Bob) to make software designs more maintainable, scalable, and robust. It stands for:

1. **S**ingle Responsibility Principle (SRP)
2. **O**pen/Closed Principle (OCP)
3. **L**iskov Substitution Principle (LSP)
4. **I**nterface Segregation Principle (ISP)
5. **D**ependency Inversion Principle (DIP)

## Single Responsibility Principle (SRP)

**Definition**: A class (or struct) should have one and only one reason to change.

Before applying SRP:

```go
type Report struct{}

func (r Report) GenerateReport() {
    fmt.Println("Generating report...")
}

func (r Report) SaveToFile(filename string) {
    fmt.Println("Saving report to file:", filename)
}
```

After applying SRP:

```go
type ReportGenerator struct{}

func (r ReportGenerator) GenerateReport() {
    fmt.Println("Generating report...")
}

type FileSaver struct{}

func (f FileSaver) SaveToFile(filename string) {
    fmt.Println("Saving to file:", filename)
}
```

**Explanation**: Each struct has a single responsibility now: one generates the report, and another saves it.

## Open/Closed Principle (OCP)

**Definition**: Software entities should be open for extension but closed for modification.

Before OCP:

```go
type AreaCalculator struct{}

func (a AreaCalculator) CalculateArea(shape string, dimensions ...float64) float64 {
    if shape == "circle" {
        return 3.14 * dimensions[0] * dimensions[0]
    } else if shape == "rectangle" {
        return dimensions[0] * dimensions[1]
    }
    return 0
}
```

After OCP (Extensible with interfaces):

```go
type Shape interface {
    Area() float64
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return 3.14 * c.Radius * c.Radius
}

type Rectangle struct {
    Length, Width float64
}

func (r Rectangle) Area() float64 {
    return r.Length * r.Width
}

type AreaCalculator struct{}

func (a AreaCalculator) CalculateArea(s Shape) float64 {
    return s.Area()
}
```

**Explanation**: New shapes can be added by implementing the `Shape` interface without modifying `AreaCalculator`.

## Liskov Substitution Principle (LSP)

**Definition**: Subtypes must be substitutable for their base types without altering the correctness of the program.

Before LSP:

```go
type Bird struct{}

func (b Bird) Fly() {
    fmt.Println("Flying")
}

type Penguin struct {
    Bird
}

func (p Penguin) Fly() {
    panic("Penguins cannot fly!")
}
```

After LSP:

```go
type Bird interface {
    Move()
}

type Sparrow struct{}

func (s Sparrow) Move() {
    fmt.Println("Flying")
}

type Penguin struct{}

func (p Penguin) Move() {
    fmt.Println("Swimming")
}

func main() {
    var bird Bird
    bird = Sparrow{}
    bird.Move() // Output: Flying

    bird = Penguin{}
    bird.Move() // Output: Swimming
}
```

**Explanation**: The `Bird` interface represents general behavior, and specific birds implement it in ways that don't break substitutability.

## Interface Segregation Principle (ISP)

**Definition**: Clients should not be forced to depend on methods they do not use.

Before ISP:

```go
type Worker interface {
    Work()
    Eat()
}

type HumanWorker struct{}

func (h HumanWorker) Work() {
    fmt.Println("Working...")
}

func (h HumanWorker) Eat() {
    fmt.Println("Eating lunch...")
}

type RobotWorker struct{}

func (r RobotWorker) Work() {
    fmt.Println("Working...")
}

func (r RobotWorker) Eat() {
    // Robots don't eat
    panic("Robots don't eat!")
}
```

After ISP:

```go
type Worker interface {
    Work()
}

type Eater interface {
    Eat()
}

type HumanWorker struct{}

func (h HumanWorker) Work() {
    fmt.Println("Working...")
}

func (h HumanWorker) Eat() {
    fmt.Println("Eating lunch...")
}

type RobotWorker struct{}

func (r RobotWorker) Work() {
    fmt.Println("Working...")
}

func main() {
    human := HumanWorker{}
    robot := RobotWorker{}

    human.Work() // Output: Working...
    human.Eat()  // Output: Eating lunch...

    robot.Work() // Output: Working...
    // robot.Eat() // Error: Robots don't eat!
}
```

**Explanation**:\
Interfaces are split based on specific needs, so `RobotWorker` doesn’t need to implement `Eat()`.

## Dependency Inversion Principle (DIP)

**Definition**: High-level modules should not depend on low-level modules. Both should depend on abstractions.

Before DIP:

```go
type MySQLDatabase struct{}

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

type Service struct {
    Database MySQLDatabase
}

func (s Service) PerformTask() {
    s.Database.Connect()
    fmt.Println("Performing task...")
}
```

After DIP:

```go
type Database interface {
    Connect()
}

type MySQLDatabase struct{}

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

type PostgreSQLDatabase struct{}

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

type Service struct {
    Database Database
}

func (s Service) PerformTask() {
    s.Database.Connect()
    fmt.Println("Performing task...")
}

func main() {
    mysql := MySQLDatabase{}
    postgres := PostgreSQLDatabase{}

    service := Service{Database: mysql}
    service.PerformTask() // Output: Connecting to MySQL... Performing task...

    service.Database = postgres
    service.PerformTask() // Output: Connecting to PostgreSQL... Performing task...
}
```

**Explanation**: The `Service` depends on the `Database` interface, not specific implementations. This makes it easy to switch databases.

## Summary

| Principle | Description                                           | Example                                            |
| --------- | ----------------------------------------------------- | -------------------------------------------------- |
| SRP       | One responsibility per class/module                   | Separate report generation and saving logic        |
| OCP       | Open for extension, closed for modification           | Add new shapes without changing the calculator     |
| LSP       | Subtypes replace base types without breaking behavior | Penguins swim instead of fly                       |
| ISP       | Split interfaces to avoid unused methods              | Separate `Work` and `Eat` interfaces               |
| DIP       | Depend on abstractions, not concretions               | Service uses `Database` interface, not specific DB |

These principles make code cleaner, more maintainable, and easier to test.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://bagus-cahyono.gitbook.io/programming-notes/programming/solid.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
