Robert C. Martin menjelaskan:
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
Open for extension artinya kode dapat dapat diperluas sesuai kebutuhan. Closed for modification artinya kode tidak dapat dirubah. Dengan kata lain, kita dapat menambah fungsionalitas pada sistem tanpa mengubah kode yang sudah ada.
Dave Cheney menjelaskan:
The Open / Closed Principle encourages you to compose simple types into more complex ones using embedding.
Agar lebih jelasnya, perhatikan kode yang ada pada contoh SRP 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{}, // 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 ID
user, err := userService.GetUserByID(1)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("User:", user)
}
Kita asumsikan kode di atas menggunakan basis data MySQL. Jika ada perubahan untuk mengganti basis data menjadi Oracle akan ada beberapa kemungkinan.
- Mengubah seluruh kode agar dapat berinteraksi dengan basis data Oracle
- Menambah method
GetUserByIDAtOracle
danSaveAtOracle
Agar dapat menerapkan OCP, kode harus dibuat seperti berikut:
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 di atas ditambahkan interface UserRepository
, lalu struct UserMySQLRepository
akan mengimplementasikan method yang ada pada interface tersebut. Kode UserMySQLRepository
berisi kode spesifik untuk berinteraksi dengan basis data MySQL. Saat menginisialisasi Jika akan mengubah basis data menjadi Oracle, maka kode akan menjadi seperti berikut:
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
}
type UserOracleRepository struct{}
func (r *UserOracleRepository) GetUserByUsername(username string) (User, error) {
// Logika untuk mengambil pengguna berdasarkan username
return User{}, nil
}
func (r *UserOracleRepository) 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
}
// GetUserByID mengambil pengguna berdasarkan ID dari Repository.
func (us *UserService) GetUserByUsername(username string) (User, error) {
return us.Repository.GetUserByUsername(username)
}
func main() {
// Inisialisasi layanan pengguna
userService := UserService{
Repository: &UserOracleRepository{}, // 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 di atas ditambahkan struct bernama UserOracleRepository
, yang didalamnya berisi kode spesifik untuk berinteraksi dengan basis data oracle. Struct UserOracleRepository
dimasukan saat inisialiasi UserService
.
Dengan OCP kita dapat menambah fungsionalitas tanpa mengubah kode yang sudah ada.
Top comments (0)