Dependency Inversion Principle (DIP)

drajatisme

drajatisme

Posted on September 8, 2023

Dependency Inversion Principle (DIP)

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)
}
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

Kode diatas telah mematuhi DIP, karena UserService telah bergantung pada interface UserRepository.

Referensi

💖 💪 🙅 🚩
drajatisme
drajatisme

Posted on September 8, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related