Langsung ke konten utama

SOLID - Prinsip Desain Arsitektur Di Dalam Pemrograman Object Oriented (Dependency Inversion Principal)

Akhirnya kita sampai di bagian paling akhir dari seri tulisan SOLID Principal yaitu untuk huruf D. 

Ketika kita bekerja menggunakan bahasa pemrograman berorientasi object, kita pasti akan menggunakan Class. Class tersebut pada akhirnya akan saling terkait /  ketergantungan satu dan yang lain untuk melakukan tugas tertentu.  Ketergantungan ini disebut dengan istilah Dependency. Dependency ini bisa terjadi antar Class di dalam satu Code Base yang sama, atau bahkan antar Library. Controhnya seperti potongan kode berikut.
<?php

namespace App\DependencyInversion;

/**
 * Description of Engine
 *
 * @author programmer
 */
class Engine 
{
    
    private KeihinCarburator $carburator;
    
    private NGKSpark $spark;
    
    public function __construct(KeihinCarburator $carburator, NGKSpark $spark) 
    {
        $this->carburator = $carburator;
        $this->spark = $spark;
    }
    
    public function start():bool
    {
        $this->carburator->open();
        $this->spark->nyalakan();
    }
    
}

class KeihinCarburator
{
    public function open():?string
    {
        return "karburator terbuka";
    }
}

class NGKSpark
{
    public function nyalakan():?string
    {
        return "busi menyala";
    }
}
Pada kode di atas, terlihat bahwa Class Mesin membutuhkan object dari Class KeihinCarburator dan Class NGKSpark untuk bisa berjalan sebagaimana mestinya.
<?php

namespace App\DependencyInversion;

class MotorManager
{
    public static function createEngine():Engine 
    {
        return new Engine(new KeihinCarburator(), new NGKSpark());
    }
}

Latar Belakang

  • High-level modules should not depend on low-level modules. Both should depend on abstractions. 
  • Abstractions should not depend on details. Details should depend on abstractions.
Kode yang saya tulis diatas memiliki ketergantungan yang kuat antara Class Mesin dengan Class KeihinCarburator juga Class NGKSpark. Ketergantungan yang kuat (tighly-coupled) seperti ini perlu dihindari agar kode yang kita punya menjadi lebih mudah untuk di maintenance juga dikembangkan.  

Sekarang mari kita coba bayangkan skenarionya.

Andaikan kode diatas sudah berjalan dan motor sudah berfungsi sebagaimana mestinya. Kemudian suatu hari Busi dari motor tersebut mati dan harus diganti yang baru. Namun, dipasaran sudah tidak ada stok busi Merk NGK yang ternyata sudah dihentikan produksinya. Akhirnya mau tidak mau kita harus memodifikasi Class Mesin agar support dengan Busi jenis IgnimaxSpark. Kemudian masalah lain muncul karena motor sudah diproduksi dan dipakai oleh ribuan orang. Apakah akhirnya kita akan menarik produk motor tersebut dan menggantinya dengan Motor yang baru ? tentu tenaga dan biaya akan sangat mahal hanya karena masalah Busi yang tidak support. 

Sekarang kembali ke koding, apabila Class Mesin sudah di pakai di ratusan kode kita yang lain, tentu akan merepotkan jika kita harus menggantinya satu per satu di semua tempat agar kode kita dapat berjalan ?

Karena permasalahan tersebut, maka dicetuskan konsep Dependency Inversion. Dependency Inversion singkatnya adalah merubah ketergantungan antar Class tersebut kedalam bentuk abstraksi.

Solusi

Didalam Dependency Inversion, Class yang terdapat di dalam module High Level tidak boleh tergantung pada Class yang terdapat di module Low Level. 

High Level disini merupakan modul yang memuat proses bisnis utama yang merupakan suatu proses yang melakukan tugas utama tertentu. Di dalam kasus kode di atas, High Level modul kita adalah Class Engine yang berfungsi menjalankan fungsi utama sebuah mesin. Atau bisa jadi merupakan library pihak ketiga yang kita tambahkan di project kita.

Low Level module merupakan kode yang memuat implementasi dari bagian-bagian tertentu yang mungkin akan punya banyak variasi. Didalam kasus kode kita, Low level merupakan kode yang memuat kode sparepart seperti karburator, busi dll yang kemungkinan bisa kita ganti-ganti sesuai kebutuhan. Atau kalau di kode pada umumnya ini adalah kode yang kita tulis di folder src.

High Level disini tidak boleh tergantung terhadap Class yang terdapat di Low Level. Karena itu, Class Engine kita ubah menjadi seperti berikut.
<?php

interface CarburatorInterface
{
    public function open():?string;
}

interface SparkInterface 
{
    public function nyalakan():?string;
}

abstract class Engine 
{
    private CarburatorInterface $carburator;
    
    private SparkInterface $spark;
    
    public function __construct(CarburatorInterface $carburator, SparkInterface $spark) 
    {
        $this->carburator = $carburator;
        $this->spark = $spark;
    }
    
    public function start():bool
    {
        $this->carburator->open();
        $this->spark->nyalakan();
    }
}
Sekarang Class Engine mempunyai ketergantungan tidak lagi kepada Concrete Class, tetapi sekarang terhadap abstraksi yang dalam kasus ini terhadap interface CarburatorInterface dan SparkInterface. Jadi Class Engine bisa lebih fleksibel.

Kemudian untuk Low Level Module kita modifikasi menjadi beberapa Class implementasi dari interface yang terdapat dari module High Level di atas.
<?php

class HondaEngine extends Engine
{
    
}

class YamahaEngine extends Engine
{
    
}

class KeihinCarburator implements CarburatorInterface
{
    public function open():?string
    {
        return "karburator terbuka";
    }
}

class NGKSpark implements SparkInterface
{
    public function nyalakan():?string
    {
        return "NGK => busi menyala";
    }
}

class HondaSpark implements SparkInterface 
{
    public function nyalakan():?string
    {
        return "Honda => busi menyala";
    }
}

pada kode di atas, saya menambahkan 2 buah Concrete Class dari interface SparkInterface. Kita sudah punya 2 buah Busi yaitu Merk NGK dan Honda. JUga punya dua buah mesin yaitu Honda dan Yamaha. 

kemudian di class Manager kita sebagai pengguna bisa dengan leluasa bongkar pasang Class sesuai dengan kebutuhan seperti kode berikut.
<?php

class EngineManager
{
    public static function createHondaEngine():Engine 
    {
        return new HondaEngine( 
            new KeihinCarburator(), new HondaSpark()
        );
    }
    
    public static function createYamahaEngine():Engine 
    {
        return new YamahaEngine( 
            new KeihinCarburator(), new NGKSpark()
        );
    }
    
    public static function createHondaCustomEngine():Engine 
    {
        return new HondaEngine( 
            new KeihinCarburator(), new NGKSpark()
        );
    }
}

Kesimpulan

Akhirnya dengan prinsip Dependency Inversion, kode yang kita buat menjadi lebih mudah untuk di kembangkan juga perbaiki. Ini juga memudahkan kita untuk mekerja secara kolaborasi juga membagikan kode dengan developer lain secara lebih mudah.

untuk tulisan terkait SOLID yang lain: 

Komentar

Postingan populer dari blog ini

Contoh Perhitungan Algoritma Perceptron

      Melanjutkan tulisan saya sebelumnya tentang algoritma perceptron,kali ini saya akan menulis tentang conto perhitungan manual algoritma perceptron. Untuk contoh kasusnya saya menggunakan data logika AND. Cekidot.... Algoritma      Data yang kita gunakan sebagai contoh adalah data logika AND sebagai berikut: x1 x2 target 0 0 0 0 1 0 1 0 0 1 1 1       tentukan bobot awal secara acak, saya pakai contoh w1 = 0,w2 =0, learning rate = 1, bias = 0,maksimal epoh = 10. Disini saya memakai fungsi aktivasi undak biner. Epoh ke 1 Data ke satu x = {0,0}, bobot w = {0,0},b=0,target = 0 y_in = (x1*w1)+(x2*w2)+b = (0*0)+(0*0)+0 = 0 y = sign(0) = 1 karena y != target maka hitung error dan update bobot  error = target - y = 0 - 1 = -1 w1_baru = w1_lama +(learning_rate*error*x1)                = 0 + (1*(-1)*0) = 0 w2_baru = w2_lama +(learning_rate*error*x2)                = 0+(1*(-1)*0) = 0

Contoh Perhitungan Algoritma Learning Vector Quantization

Melanjutkan tulisan saya tentang algoritma Learning Vector Quantization yang lalu, kali ini saya akan melanjutkan dengan contoh perhitungan manual. Berikut ini contoh data yang akan kita hitung. No X1 X2 X3 X4 target 1 0 1 1 0 0 2 0 0 1 1 1 3 1 1 1 1 0 4 1 0 0 1 1 pada contoh di atas, saya menggunakan 4 data sebagai data training beserta target yang bertujuan untuk mendapatkan bobot yang akan digunakan pada proses klasifikasi. Bobot awal adalah { 1, 1, 1, 0} dan { 1, 0, 1, 1} dengan learning rate 0,05 dengan fungsi pembelajaran = 0,1. Pelatihan Iterasi ke 1 1. Data ke 1 { 0, 1, 1, 0} dengan target 0, bobot = {{ 1, 1, 1, 0},{ 1, 0, 1, 1}}      - menghitung bobot untuk masing masing output :          kelas 0 = sqrt(((0-1)^2)+((1-1)^2)+((1-1)^2)+((0-0)^2)) = 1         kelas 1 = sqrt(((0-1)^2)+((1-0)^2)+((1-

Pemrograman Berorientasi Object - Overloading dan Overriding

       Function atau method overloading dan override adalah fitur yang sangat mendasar dan berguna dari bahasa OOP manapun. Dalam tutorial ini kita akan membahas implementasi metode overloading dan override di php. Di sini pertama kita akan membahas dasar-dasar overloading dan override. Setelah eksplorasi dasar kita akan menerapkan overloading dan override di php. Sebelum melangkah lebih jauh, saya mengasumsikan bahwa Anda memiliki pengetahuan dasar tentang class dan pewarisan di php. Anda juga memiliki pemahaman tentang magic method di php. Magic method karena overloading di php bisa di implmentasikan dengan menggunakan magic method. Overriding        Arti dasar dari overriding di OOP sama dengan arti kata sebenarnya. Dalam arti kata sebenarnya dari overriding adalah menggantikan perilaku orang tua yang sama pada anak. Ini sama dengan override method di OOP. Dalam arti OOP, override adalah mengganti method class induk di class anak. Atau dengan method kata kunci sederhana yang me