SOLID

SOLID #

Prinsip SOLID adalah kumpulan lima prinsip desain berorientasi objek yang bertujuan untuk membuat kode lebih mudah dipahami, dirawat, dikembangkan, dan diuji. Prinsip ini pertama kali diperkenalkan oleh Robert C. Martin (Uncle Bob) dan hingga hari ini masih menjadi fondasi penting dalam software engineering modern.

Walaupun Go (Golang) bukan bahasa OOP klasik seperti Java atau C++, prinsip SOLID tetap sangat relevan karena Go mendukung interface, composition, dan loose coupling.

Artikel ini akan membahas:

  • Apa itu SOLID dan tujuannya
  • Masalah yang ingin diselesaikan SOLID
  • Penjelasan tiap prinsip (S, O, L, I, D)
  • Contoh implementasi realistis menggunakan Golang

Mengapa SOLID Itu Penting? #

Tanpa prinsip desain yang baik, kode cenderung:

  • Sulit diubah karena perubahan kecil berdampak besar
  • Penuh if-else dan switch kompleks
  • Sulit di-test (terutama unit test)
  • Menghasilkan technical debt

SOLID membantu kita menulis kode yang:

  • Low coupling
  • High cohesion
  • Mudah di-extend tanpa merusak kode lama
  • Mudah di-mock dan di-test

S — Single Responsibility Principle (SRP) #

Satu module / struct hanya boleh punya satu alasan untuk berubah.

❌ Contoh Buruk (Melanggar SRP) #

// UserService menangani terlalu banyak tanggung jawab
type UserService struct {}

func (s *UserService) CreateUser(name string) {
    // logic create user
}

func (s *UserService) SaveToDatabase() {
    // database logic
}

func (s *UserService) SendWelcomeEmail() {
    // email logic
}

Masalah:

  • Logic bisnis, database, dan email tercampur
  • Sulit di-test dan di-maintain

✅ Contoh Baik (Menerapkan SRP) #

type UserService struct {
    repo   UserRepository
    sender EmailSender
}

func (s *UserService) CreateUser(name string) {
    s.repo.Save(name)
    s.sender.SendWelcome(name)
}

Setiap komponen punya tanggung jawab jelas.


O — Open/Closed Principle (OCP) #

Software entity harus terbuka untuk ekstensi, tapi tertutup untuk modifikasi.

❌ Contoh Buruk #

func CalculateDiscount(userType string, price float64) float64 {
    if userType == "VIP" {
        return price * 0.8
    }
    if userType == "Member" {
        return price * 0.9
    }
    return price
}

Setiap tipe baru → ubah fungsi lama.

✅ Contoh Baik dengan Interface #

type DiscountStrategy interface {
    Apply(price float64) float64
}

type VIPDiscount struct {}
func (d VIPDiscount) Apply(price float64) float64 {
    return price * 0.8
}

type MemberDiscount struct {}
func (d MemberDiscount) Apply(price float64) float64 {
    return price * 0.9
}

Menambah diskon baru cukup dengan struct baru.


L — Liskov Substitution Principle (LSP) #

Object turunan harus bisa menggantikan object induknya tanpa merusak behavior.

❌ Contoh Pelanggaran LSP #

type Bird interface {
    Fly()
}

type Penguin struct {}
func (p Penguin) Fly() {
    panic("penguin can't fly")
}

Penguin tidak bisa menggantikan Bird dengan aman.

✅ Contoh Perbaikan #

type Bird interface {
    Eat()
}

type FlyingBird interface {
    Bird
    Fly()
}

Interface dipisah berdasarkan kemampuan.


I — Interface Segregation Principle (ISP) #

Jangan memaksa client bergantung pada interface yang tidak mereka gunakan.

❌ Interface Terlalu Gemuk #

type Worker interface {
    Work()
    Eat()
}

Robot dipaksa implement Eat().

✅ Interface Kecil & Spesifik #

type Worker interface {
    Work()
}

type Eater interface {
    Eat()
}

Lebih fleksibel dan idiomatik di Go.


D — Dependency Inversion Principle (DIP) #

High-level module tidak boleh bergantung pada low-level module, keduanya harus bergantung pada abstraction.

❌ Contoh Buruk #

type MySQL struct {}

func (db MySQL) Save(data string) {}

type Service struct {
    db MySQL
}

Sulit diganti dan sulit di-test.

✅ Contoh Baik #

type Database interface {
    Save(data string)
}

type MySQL struct {}
func (db MySQL) Save(data string) {}

type Service struct {
    db Database
}

Dependency bisa di-inject (mock, postgres, dll).


SOLID dan Dependency Injection di Go #

Go sangat cocok dengan SOLID karena:

  • Interface kecil
  • Composition over inheritance
  • Dependency injection via constructor
func NewService(db Database) *Service {
    return &Service{db: db}
}

Ini membuat unit test jauh lebih mudah.


Kapan Tidak Perlu Memaksakan SOLID? #

  • Aplikasi sangat kecil / script sekali pakai
  • Over-abstraction tanpa kebutuhan nyata

Gunakan SOLID secukupnya, bukan dogma.


Kesimpulan #

Prinsip SOLID membantu kita:

  • Menulis kode yang bersih dan scalable
  • Mengurangi technical debt
  • Mempermudah testing dan refactoring

Walaupun berasal dari OOP klasik, SOLID sangat relevan untuk Golang jika diterapkan dengan pendekatan idiomatik.

Good design is about managing change. — Robert C. Martin

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact