SOLID
Lima prinsip desain oleh Robert C. Martin β Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion.
Versi Bahasa Indonesia. Baca dalam Bahasa Inggris
adalah seperangkat lima prinsip desain yang diperkenalkan oleh Robert C. Martin (Uncle Bob) untuk membuat desain perangkat lunak lebih mudah dipelihara, diskalakan, dan robust. Ini adalah singkatan dari:
- Single Responsibility Principle (SRP)
- Open/Closed Principle (OCP)
- Liskov Substitution Principle (LSP)
- Interface Segregation Principle (ISP)
- Dependency Inversion Principle (DIP)
Single Responsibility Principle (SRP)
Definisi: Sebuah kelas (atau struct) harus memiliki satu dan hanya satu alasan untuk berubah.
Sebelum menerapkan SRP:
type Report struct{}
func (r Report) GenerateReport() {
fmt.Println("Generating report...")
}
func (r Report) SaveToFile(filename string) {
fmt.Println("Saving report to file:", filename)
}Setelah menerapkan SRP:
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)
}Penjelasan: Setiap struct sekarang memiliki satu tanggung jawab: satu menghasilkan laporan, dan yang lainnya menyimpannya.
Open/Closed Principle (OCP)
Definisi: Entitas perangkat lunak harus terbuka untuk ekstensi tetapi tertutup untuk modifikasi.
Sebelum OCP:
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
}Setelah OCP (Dapat diperluas dengan interface):
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()
}Penjelasan: Bentuk baru dapat ditambahkan dengan mengimplementasikan interface Shape tanpa memodifikasi AreaCalculator.
Liskov Substitution Principle (LSP)
Definisi: Subtipe harus dapat disubstitusi untuk tipe dasarnya tanpa mengubah kebenaran program.
Sebelum LSP:
type Bird struct{}
func (b Bird) Fly() {
fmt.Println("Flying")
}
type Penguin struct {
Bird
}
func (p Penguin) Fly() {
panic("Penguins cannot fly!")
}Setelah LSP:
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
}Penjelasan: Interface Bird mewakili perilaku umum, dan burung tertentu mengimplementasikannya dengan cara yang tidak merusak substitutability.
Interface Segregation Principle (ISP)
Definisi: Klien tidak boleh dipaksa untuk bergantung pada method yang tidak mereka gunakan.
Sebelum ISP:
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() {
// Robot tidak makan
panic("Robots don't eat!")
}Setelah ISP:
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: Robot tidak makan!
}Penjelasan: Interface dipecah berdasarkan kebutuhan spesifik, sehingga RobotWorker tidak perlu mengimplementasikan Eat().
Dependency Inversion Principle (DIP)
Definisi: Modul tingkat tinggi tidak boleh bergantung pada modul tingkat rendah. Keduanya harus bergantung pada abstraksi.
Sebelum DIP:
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...")
}Setelah DIP:
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...
}Penjelasan: Service bergantung pada interface Database, bukan implementasi spesifik. Ini memudahkan untuk mengganti database.
Ringkasan
| Prinsip | Deskripsi | Contoh |
|---|---|---|
| SRP | Satu tanggung jawab per kelas/modul | Pisahkan logika pembuatan dan penyimpanan laporan |
| OCP | Terbuka untuk ekstensi, tertutup untuk modifikasi | Tambahkan bentuk baru tanpa mengubah kalkulator |
| LSP | Subtipe menggantikan tipe dasar tanpa merusak perilaku | Penguin berenang, bukan terbang |
| ISP | Pisahkan interface untuk menghindari method yang tidak digunakan | Pisahkan interface Work dan Eat |
| DIP | Bergantung pada abstraksi, bukan implementasi konkret | Service menggunakan interface Database, bukan DB spesifik |
Prinsip-prinsip ini membuat kode lebih bersih, lebih mudah dipelihara, dan lebih mudah diuji.
Last updated on