Dependency Inversion Principle (DIP)
drajatisme
Posted on September 8, 2023
Robert C. Martin menjelaskan:
A. High level modules should not depend upon low level modules. both should depend upon abstractions.
B. Abstractions should not depend upon details. details should depend upon abstractions.
Dave Cheney menjelaskan:
The Dependency Inversion Principle encourages you move the knowledge of the things your package depends on from compile time–in Go we see this with a reduction in the number of import statements used by a particular package–to run time.
Mari kita lihat kode yang melanggar DIP, berdasarkan kode yang melanggar OCP sebelumnya:
package main
import (
"fmt"
)
// User adalah tipe data yang mewakili informasi pengguna.
type User struct {
ID int
Username string
Email string
}
// UserRepository bertanggung jawab untuk berinteraksi dengan basis data pengguna.
type UserRepository struct{}
func (us *UserRepository) GetUserByUsername(username string) (User, error) {
// Logika untuk mengambil pengguna berdasarkan username
return User{}, nil
}
func (us *UserRepository) Save() error {
// Logika untuk menyimpan ke basis data
return nil
}
// UserValidator bertanggung jawab untuk memvalidasi data pengguna.
type UserValidator struct{}
func (uv *UserValidator) Validate() error {
// Logika validasi pengguna seperti validasi email, kata sandi, dsb.
return nil
}
// UserService adalah layanan yang berfungsi sebagai penghubung antara klien dan data pengguna.
type UserService struct {
Repository UserRepository
Validator UserValidator
}
// CreateUser membuat pengguna baru dengan menggunakan Repository dan Validator.
func (us *UserService) CreateUser(user User) error {
// Validasi pengguna menggunakan Validator
if err := us.Validator.Validate(); err != nil {
return err
}
// Simpan pengguna ke basis data menggunakan Repository
if err := us.Repository.Save(); err != nil {
return err
}
return nil
}
func (us *UserService) GetUserByID(userID int) (User, error) {
// GetUserByID mengambil pengguna berdasarkan ID dari Repository.
return us.Repository.GetUserByID(userID)
}
func main() {
// Inisialisasi layanan pengguna
userService := UserService{
Repository: UserRepository{}, // Melanggar DIP
Validator: UserValidator{},
}
// Membuat pengguna baru
newUser := User{ID: 1, Username: "john_doe", Email: "john@example.com"}
err := userService.CreateUser(newUser)
if err != nil {
fmt.Println("Error:", err)
return
}
// Mengambil pengguna berdasarkan ID
user, err := userService.GetUserByID(1)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("User:", user)
}
Berdasarkan kode diatas terlihat bahwa modul tingkat atas (UserService
) bergantung kepada modul tingkat bawah (UserRepository
). Untuk mematuhi DIP, UserService
harus bergantung pada abstraksi (interface
), dan UserRepository
harus mengimplementasikan interface tersebut. Mari kita lihat kode yang mematuhi DIP, berdasarkan kode yang mematuhi OCP sebelumnya:
package main
import (
"fmt"
)
// User adalah tipe data yang mewakili informasi pengguna.
type User struct {
ID int
Username string
Email string
}
type UserRepository interface {
GetUserByUsername(username string) (User, error)
Save() error
}
// UserRepository bertanggung jawab untuk berinteraksi dengan basis data pengguna.
type UserMySQLRepository struct{}
func (r *UserMySQLRepository) GetUserByUsername(username string) (User, error) {
// Logika untuk mengambil pengguna berdasarkan username
return User{}, nil
}
func (r *UserMySQLRepository) Save() error {
// Logika untuk menyimpan ke basis data
return nil
}
// UserValidator bertanggung jawab untuk memvalidasi data pengguna.
type UserValidator struct{}
func (uv *UserValidator) Validate() error {
// Logika validasi pengguna seperti validasi email, kata sandi, dsb.
return nil
}
// UserService adalah layanan yang berfungsi sebagai penghubung antara klien dan data pengguna.
type UserService struct {
Repository UserRepository
Validator UserValidator
}
// CreateUser membuat pengguna baru dengan menggunakan Repository dan Validator.
func (us *UserService) CreateUser(user User) error {
// Validasi pengguna menggunakan Validator
if err := us.Validator.Validate(); err != nil {
return err
}
// Simpan pengguna ke basis data menggunakan Repository
if err := us.Repository.Save(); err != nil {
return err
}
return nil
}
// GetUserByUsername mengambil pengguna berdasarkan username.
func (us *UserService) GetUserByUsername(username string) (User, error) {
return us.Repository.GetUserByUsername(username)
}
func main() {
// Inisialisasi layanan pengguna
userService := UserService{
Repository: &UserMySQLRepository{}, // Contoh sederhana, seharusnya menggunakan koneksi basis data nyata.
Validator: UserValidator{}, // Contoh sederhana, seharusnya ada validasi yang lebih lengkap.
}
// Membuat pengguna baru
newUser := User{ID: 1, Username: "john_doe", Email: "john@example.com"}
err := userService.CreateUser(newUser)
if err != nil {
fmt.Println("Error:", err)
return
}
// Mengambil pengguna berdasarkan username
user, err := userService.GetUserByUsername("myUser")
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("User:", user)
}
Kode diatas telah mematuhi DIP, karena UserService
telah bergantung pada interface UserRepository
.
Referensi
Posted on September 8, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.