it-swarm-id.com

Apa prinsip inversi ketergantungan dan mengapa itu penting?

Apa prinsip inversi ketergantungan dan mengapa itu penting?

163
Phillip Wells

Periksa dokumen ini: Prinsip Pembalikan Ketergantungan .

Pada dasarnya dikatakan:

  • Modul tingkat tinggi tidak boleh bergantung pada modul tingkat rendah. Keduanya harus bergantung pada abstraksi.
  • Abstraksi tidak boleh bergantung pada detail. Detail harus bergantung pada abstraksi.

Singkatnya, mengapa itu penting, perubahan itu berisiko, dan dengan bergantung pada konsep alih-alih pada implementasi, Anda mengurangi kebutuhan akan perubahan di situs panggilan.

Secara efektif, DIP mengurangi kopling antara potongan kode yang berbeda. Idenya adalah bahwa meskipun ada banyak cara untuk mengimplementasikan, katakanlah, fasilitas logging, cara Anda akan menggunakannya harus relatif stabil dalam waktu. Jika Anda dapat mengekstrak antarmuka yang mewakili konsep logging, antarmuka ini akan jauh lebih stabil dalam waktu daripada implementasinya, dan situs panggilan harus jauh lebih sedikit dipengaruhi oleh perubahan yang Anda buat sambil mempertahankan atau memperluas mekanisme logging tersebut.

Dengan juga membuat implementasi bergantung pada sebuah antarmuka, Anda mendapatkan kemungkinan untuk memilih pada saat run-time implementasi mana yang lebih cocok untuk lingkungan khusus Anda. Tergantung pada kasusnya, ini mungkin menarik juga.

102
Carl Seleborg

Ketika kita merancang aplikasi perangkat lunak, kita dapat mempertimbangkan kelas tingkat rendah kelas yang mengimplementasikan operasi dasar dan primer (akses disk, protokol jaringan, ...) dan kelas tingkat tinggi kelas yang merangkum logika kompleks (arus bisnis, ...).

Yang terakhir bergantung pada kelas tingkat rendah. Cara alami untuk menerapkan struktur seperti itu adalah dengan menulis kelas tingkat rendah dan begitu kita memilikinya untuk menulis kelas tingkat tinggi yang kompleks. Karena kelas tingkat tinggi didefinisikan dalam hal orang lain, ini tampaknya cara logis untuk melakukannya. Tapi ini bukan desain yang fleksibel. Apa yang terjadi jika kita perlu mengganti kelas tingkat rendah?

Prinsip Pembalikan Ketergantungan menyatakan bahwa:

  • Modul tingkat tinggi seharusnya tidak tergantung pada modul tingkat rendah. Keduanya harus bergantung pada abstraksi.
  • Abstraksi tidak harus bergantung pada detail. Detail harus bergantung pada abstraksi.

Prinsip ini berusaha untuk "membalikkan" gagasan konvensional bahwa modul tingkat tinggi dalam perangkat lunak harus bergantung pada modul tingkat bawah. Di sini modul tingkat tinggi memiliki abstraksi (misalnya, menentukan metode antarmuka) yang diimplementasikan oleh modul tingkat bawah. Dengan demikian membuat modul level bawah tergantung pada modul level yang lebih tinggi.

10
nikhil.singhal

Ketergantungan inversi diterapkan dengan baik memberikan fleksibilitas dan stabilitas di tingkat seluruh arsitektur aplikasi Anda. Ini akan memungkinkan aplikasi Anda berkembang lebih aman dan stabil.

Arsitektur berlapis tradisional

Secara tradisional UI arsitektur berlapis tergantung pada lapisan bisnis dan ini pada gilirannya tergantung pada lapisan akses data.

http://xurxodev.com/content/images/2016/02/Traditional-Layered.png

Anda harus memahami layer, paket, atau pustaka. Mari kita lihat bagaimana kodenya.

Kami akan memiliki pustaka atau paket untuk lapisan akses data.

// DataAccessLayer.dll
public class ProductDAO {

}

Dan perpustakaan lain atau logika paket bisnis lapisan yang bergantung pada lapisan akses data.

// BusinessLogicLayer.dll
using DataAccessLayer;
public class ProductBO { 
    private ProductDAO productDAO;
}

Arsitektur berlapis dengan inversi ketergantungan

Pembalikan ketergantungan menunjukkan hal berikut:

Modul tingkat tinggi tidak harus bergantung pada modul tingkat rendah. Keduanya harus bergantung pada abstraksi.

Abstraksi tidak harus bergantung pada detail. Detail harus bergantung pada abstraksi.

Apa saja modul tingkat tinggi dan tingkat rendah? Modul-modul berpikir seperti perpustakaan atau paket, modul tingkat tinggi adalah modul-modul yang secara tradisional memiliki dependensi dan level rendah di mana mereka bergantung.

Dengan kata lain, modul level tinggi akan menjadi tempat tindakan dipanggil dan tingkat rendah di mana tindakan dilakukan.

Kesimpulan yang masuk akal untuk ditarik dari prinsip ini adalah bahwa seharusnya tidak ada ketergantungan antara konkretsi, tetapi harus ada ketergantungan pada abstraksi. Tetapi menurut pendekatan yang kami ambil, kami bisa menyalahgunakan ketergantungan investasi, tetapi abstraksi.

Bayangkan kita mengadaptasi kode kita sebagai berikut:

Kami akan memiliki pustaka atau paket untuk lapisan akses data yang mendefinisikan abstraksi.

// DataAccessLayer.dll
public interface IProductDAO
public class ProductDAO : IProductDAO{

}

Dan perpustakaan lain atau logika paket bisnis lapisan yang bergantung pada lapisan akses data.

// BusinessLogicLayer.dll
using DataAccessLayer;
public class ProductBO { 
    private IProductDAO productDAO;
}

Meskipun kami bergantung pada ketergantungan abstrak antara bisnis dan akses data tetap sama.

http://xurxodev.com/content/images/2016/02/Traditional-Layered.png

Untuk mendapatkan inversi dependensi, antarmuka persistensi harus didefinisikan dalam modul atau paket di mana logika atau domain tingkat tinggi ini berada dan bukan di modul tingkat rendah.

Pertama-tama tentukan apa itu lapisan domain dan abstraksi komunikasinya adalah kegigihan.

// Domain.dll
public interface IProductRepository;

using DataAccessLayer;
public class ProductBO { 
    private IProductRepository productRepository;
}

Setelah lapisan kegigihan bergantung pada domain, dapatkan untuk membalikkan sekarang jika ketergantungan ditentukan.

// Persistence.dll
public class ProductDAO : IProductRepository{

}

http://xurxodev.com/content/images/2016/02/Dependency-Inversion-Layers.png

Memperdalam prinsip

Penting untuk mengasimilasi konsep dengan baik, memperdalam tujuan dan manfaat. Jika kita tinggal secara mekanis dan mempelajari repositori kasus tipikal, kita tidak akan dapat mengidentifikasi di mana kita dapat menerapkan prinsip ketergantungan.

Tetapi mengapa kita membalikkan ketergantungan? Apa tujuan utama di luar contoh spesifik?

Biasanya memungkinkan hal-hal yang paling stabil, yang tidak bergantung pada hal-hal yang kurang stabil, berubah lebih sering.

Jenis persistensi lebih mudah diubah, baik basis data atau teknologi untuk mengakses basis data yang sama daripada logika domain atau tindakan yang dirancang untuk berkomunikasi dengan kegigihan. Karena itu, ketergantungan ini dibalik karena lebih mudah untuk mengubah kegigihan jika perubahan ini terjadi. Dengan cara ini kita tidak perlu mengubah domain. Lapisan domain adalah yang paling stabil, oleh karena itu ia tidak boleh bergantung pada apa pun.

Tetapi tidak hanya ada contoh repositori ini. Ada banyak skenario di mana prinsip ini berlaku dan ada arsitektur berdasarkan prinsip ini.

Arsitektur

Ada arsitektur di mana inversi ketergantungan adalah kunci dari definisinya. Di semua domain itu adalah yang paling penting dan itu adalah abstraksi yang akan menunjukkan protokol komunikasi antara domain dan sisa paket atau pustaka didefinisikan.

Arsitektur Bersih

Dalam Arsitektur bersih domain terletak di tengah dan jika Anda melihat ke arah panah yang menunjukkan ketergantungan, jelaslah lapisan apa yang paling penting dan stabil. Lapisan luar dianggap alat yang tidak stabil jadi hindari tergantung padanya.

Arsitektur Heksagonal

Ini terjadi dengan cara yang sama dengan arsitektur heksagonal, di mana domain juga terletak di bagian tengah dan port adalah abstraksi komunikasi dari domino ke arah luar. Di sini sekali lagi terbukti bahwa domain adalah ketergantungan yang paling stabil dan tradisional dibalik.

9
xurxodev

Bagi saya, Prinsip Ketergantungan Inversi, seperti yang dijelaskan dalam artikel resmi , benar-benar merupakan upaya yang salah arah untuk meningkatkan usabilitas modul yang secara inheren kurang dapat digunakan kembali, serta cara untuk menyelesaikan masalah dalam bahasa C++.

Masalah dalam C++ adalah bahwa file header biasanya berisi deklarasi bidang pribadi dan metode. Oleh karena itu, jika modul C++ tingkat tinggi menyertakan file header untuk modul tingkat rendah, itu akan tergantung pada yang sebenarnya pelaksanaan rincian modul itu. Dan itu, jelas, bukan hal yang baik. Tapi ini bukan masalah dalam bahasa yang lebih modern yang umum digunakan saat ini.

Modul tingkat tinggi secara inheren kurang dapat digunakan kembali daripada modul tingkat rendah karena yang pertama biasanya lebih spesifik aplikasi/konteks daripada yang terakhir. Sebagai contoh, komponen yang mengimplementasikan layar UI adalah tingkat tertinggi dan juga sangat (sepenuhnya?) Khusus untuk aplikasi. Mencoba menggunakan kembali komponen seperti itu dalam aplikasi yang berbeda adalah kontra-produktif, dan hanya dapat mengarah pada rekayasa berlebihan.

Jadi, pembuatan abstraksi terpisah pada tingkat yang sama dari komponen A yang bergantung pada komponen B (yang tidak bergantung pada A) dapat dilakukan hanya jika komponen A benar-benar akan berguna untuk digunakan kembali dalam aplikasi atau konteks yang berbeda. Jika bukan itu masalahnya, maka menerapkan DIP akan menjadi desain yang buruk.

9
Rogério

Pada dasarnya dikatakan:

Kelas harus bergantung pada abstraksi (mis. Antarmuka, kelas abstrak), bukan detail spesifik (implementasi).

8
martin.ra

Cara yang jauh lebih jelas untuk menyatakan Prinsip Ketergantungan Inversi adalah:

Modul Anda yang merangkum logika bisnis yang kompleks tidak harus bergantung langsung pada modul lain yang merangkum logika bisnis. Sebaliknya, mereka harusnya hanya bergantung pada antarmuka untuk data sederhana.

Yaitu, alih-alih mengimplementasikan kelas Anda Logic seperti yang biasanya dilakukan orang:

class Dependency { ... }
class Logic {
    private Dependency dep;
    int doSomething() {
        // Business logic using dep here
    }
}

anda harus melakukan sesuatu seperti:

class Dependency { ... }
interface Data { ... }
class DataFromDependency implements Data {
    private Dependency dep;
    ...
}
class Logic {
    int doSomething(Data data) {
        // compute something with data
    }
}

Data dan DataFromDependency harus hidup dalam modul yang sama dengan Logic, bukan dengan Dependency.

Kenapa melakukan ini?

  1. Dua modul logika bisnis sekarang dipisahkan. Ketika Dependency berubah, Anda tidak perlu mengubah Logic
  2. Memahami apa yang Logic lakukan adalah tugas yang jauh lebih sederhana: ia hanya beroperasi pada apa yang tampak seperti ADT.
  3. Logic sekarang dapat lebih mudah diuji. Anda sekarang dapat langsung instantiate Data dengan data palsu dan meneruskannya. Tidak perlu mengejek atau perancah tes yang kompleks.
5
mattvonb

Jawaban yang baik dan contoh yang baik sudah diberikan oleh orang lain di sini.

Alasan DIP penting karena memastikan prinsip OO "desain yang digabungkan secara longgar".

Objek dalam perangkat lunak Anda TIDAK boleh masuk ke hierarki di mana beberapa objek adalah yang tingkat atas, bergantung pada objek tingkat rendah. Perubahan objek tingkat rendah kemudian akan beriak ke objek tingkat atas Anda yang membuat perangkat lunak sangat rapuh untuk perubahan.

Anda ingin objek 'tingkat atas' Anda menjadi sangat stabil dan tidak rapuh untuk perubahan, oleh karena itu Anda perlu membalikkan dependensi.

5
Hace

Inversion of control (IoC) adalah pola desain di mana objek mendapatkan ketergantungannya oleh kerangka luar, daripada meminta kerangka kerja untuk ketergantungannya.

Contoh pseudocode menggunakan pencarian tradisional:

class Service {
    Database database;
    init() {
        database = FrameworkSingleton.getService("database");
    }
}

Kode serupa menggunakan IoC:

class Service {
    Database database;
    init(database) {
        this.database = database;
    }
}

Manfaat IoC adalah:

  • Anda tidak memiliki ketergantungan pada kerangka kerja .__ pusat, jadi ini dapat diubah jika Diinginkan.
  • Karena objek dibuat .__dengan injeksi, lebih disukai menggunakan antarmuka .__, mudah untuk membuat unit Tes yang menggantikan dependensi dengan.
  • Memisahkan kode off.
5
Staale

Titik inversi ketergantungan adalah membuat perangkat lunak yang dapat digunakan kembali.

Idenya adalah bahwa alih-alih dua potong kode bergantung satu sama lain, mereka bergantung pada beberapa antarmuka abstrak. Kemudian Anda dapat menggunakan kembali salah satu bagian tanpa yang lain.

Cara ini paling umum dicapai adalah melalui wadah inversi kontrol (IoC) seperti Spring di Jawa. Dalam model ini, properti objek diatur melalui konfigurasi XML alih-alih objek keluar dan menemukan ketergantungannya.

Bayangkan kodesemu ini ...

public class MyClass
{
  public Service myService = ServiceLocator.service;
}

MyClass secara langsung tergantung pada kelas Layanan dan kelas ServiceLocator. Ini membutuhkan keduanya jika Anda ingin menggunakannya di aplikasi lain. Sekarang bayangkan ini ...

public class MyClass
{
  public IService myService;
}

Sekarang, MyClass bergantung pada antarmuka tunggal, antarmuka IService. Kami membiarkan wadah IoC benar-benar mengatur nilai variabel itu.

Jadi sekarang, MyClass dapat dengan mudah digunakan kembali di proyek lain, tanpa membawa ketergantungan dua kelas lainnya bersamanya.

Lebih baik lagi, Anda tidak perlu menyeret dependensi MyService, dan dependensi dependensi itu, dan ... yah, Anda mendapatkan idenya.

1
Marc Hughes

Pembalikan Wadah Kontrol dan pola Injeksi Ketergantungan oleh Martin Fowler juga baik dibaca. Saya menemukan Head First Design Patterns buku yang luar biasa untuk peramalan pertama saya dalam mempelajari DI dan pola lainnya.

1
Chris Canal

Saya pikir saya punya contoh yang jauh lebih baik (lebih intuitif).

  • Bayangkan sebuah sistem (webapp) dengan karyawan dan manajemen kontak (dua layar).
  • Mereka tidak terkait persis sehingga Anda ingin mereka masing-masing dalam modul/folder sendiri

Jadi Anda akan memiliki beberapa titik masuk "utama" yang harus diketahui tentang modul manajemen Karyawan dan Modul manajemen kontak, dan harus menyediakan tautan dalam navigasi dan menerima permintaan api, dll. Dengan kata lain, Modul utama akan bergantung pada dua pasangan ini - ia akan tahu tentang pengontrol, rute dan tautan mereka yang harus dirender dalam navigasi (dibagi).

Contoh Node.js

// main.js
import express from 'express'

// two modules, each having many exports
import { api as contactsApi, navigation as cNav } from './contacts/'
import { api as employeesApi, navigation as eNav } from './employees/'

const api = express()
const navigation = {
  ...cNav,
  ...eNav
}

api.use('contacts', contactsApi)
api.use('employees', employeesApi)

// do something with navigation, possibly do some other setup

Juga, harap dicatat ada kasus (yang sederhana) saat ini benar-benar baik-baik saja.


Jadi seiring waktu akan sampai pada titik ketika tidak begitu sepele untuk menambahkan modul baru. Anda harus ingat untuk mendaftar api, navigasi, mungkin izin , dan main.js ini semakin besar.

Dan di situlah inversi ketergantungan muncul. Alih-alih modul utama Anda bergantung pada semua modul lain, Anda akan memperkenalkan beberapa "inti" dan membuat setiap modul mendaftar sendiri.

Jadi dalam hal ini tentang memiliki gagasan tentang ApplicationModule, yang dapat mengirimkan dirinya sendiri ke banyak layanan (rute, navigasi, izin) dan modul utama dapat tetap sederhana (cukup impor modul dan biarkan menginstalnya)

Dengan kata lain, ini tentang membuat arsitektur pluggable. Ini adalah pekerjaan ekstra dan kode yang harus Anda tulis/baca dan pertahankan sehingga Anda tidak harus melakukannya di muka tetapi saat Anda memiliki bau yang seperti ini.

Yang sangat menarik adalah Anda dapat membuat apa saja sebagai plugin, bahkan lapisan kegigihan - yang mungkin layak dilakukan jika Anda perlu mendukung banyak implementasi kegigihan, tetapi itu biasanya tidak terjadi. Lihat jawaban lain untuk gambar dengan arsitektur heksagonal, itu bagus untuk ilustrasi - ada inti dan yang lainnya pada dasarnya adalah sebuah plugin.

0
Kamil Tomšík

Ketergantungan inversi: Bergantung pada abstraksi, bukan pada konkret.

Inversi kontrol: Main vs Abstraksi, dan bagaimana Main adalah lem dari sistem.

DIP and IoC

Ini adalah beberapa posting bagus yang membicarakan ini:

https://coderstower.com/2019/03/26/dependency-inversion-why-you-shouldnt-avoid-it/

https://coderstower.com/2019/04/02/main-and-abstraction-the-decoupled-peers/

https://coderstower.com/2019/04/09/inversion-of-control-putting-all-together/

Selain jawaban lain ....

Izinkan saya menyajikan sebuah contoh terlebih dahulu ..

Biarkan ada Hotel yang meminta pasokan Generator Makanan. Hotel memberikan nama makanan (misalnya ayam) ke Generator Makanan dan Generator mengembalikan makanan yang diminta ke Hotel. Tetapi Hotel tidak peduli dengan jenis makanan yang diterimanya dan disajikan. Jadi Generator memasok makanan dengan label "Makanan" ke Hotel.

Implementasi ini di Jawa

FactoryClass dengan Metode Pabrik. Generator Makanan

public class FoodGenerator {
    Food food;
    public Food getFood(String name){
        if(name.equals("fish")){
            food =  new Fish();
        }else if(name.equals("chicken")){
            food =  new Chicken();
        }else food = null;

        return food;
    }
}


Kelas Abstrak/Antarmuka

public abstract class Food {

    //None of the child class will override this method to ensure quality...
    public void quality(){
        String fresh = "This is a fresh " + getName();
        String tasty = "This is a tasty " + getName();
        System.out.println(fresh);
        System.out.println(tasty);
    }
    public abstract String getName();
}


Ayam mengimplementasikan Makanan (Kelas Beton)

public class Chicken extends Food {
    /*All the food types are required to be fresh and tasty so
     * They won't be overriding the super class method "property()"*/

    public String getName(){
        return "Chicken";
    }
}


Ikan mengimplementasikan Makanan (Kelas Beton)

public class Fish extends Food {
    /*All the food types are required to be fresh and tasty so
     * They won't be overriding the super class method "property()"*/

    public String getName(){
        return "Fish";
    }
}


Akhirnya

Hotel

public class Hotel {

    public static void main(String args[]){
        //Using a Factory class....
        FoodGenerator foodGenerator = new FoodGenerator();
        //A factory method to instantiate the foods...
        Food food = foodGenerator.getFood("chicken");
        food.quality();
    }
}

Seperti yang Anda lihat, Hotel tidak tahu apakah itu Obyek Ayam atau Obyek Ikan. Itu hanya tahu bahwa itu adalah Obyek Makanan i.e Hotel tergantung pada Kelas Makanan.

Anda juga akan melihat bahwa Kelas Ikan dan Ayam menerapkan Kelas Makanan dan tidak terkait langsung dengan Hotel. Yaitu Ayam dan Ikan juga tergantung pada Kelas Makanan.

Yang menyiratkan bahwa komponen tingkat tinggi (Hotel) dan komponen tingkat rendah (Ikan dan Ayam) keduanya tergantung pada abstraksi (Makanan).

Ini disebut Ketergantungan Pembalikan. 

0
Revolver

Prinsip Ketergantungan Inversi (DIP) mengatakan itu 

i) Modul tingkat tinggi tidak boleh bergantung pada modul tingkat rendah. Keduanya harus bergantung pada abstraksi.

ii) Abstraksi tidak boleh bergantung pada detail. Detail harus bergantung pada abstraksi.

Contoh: 

    public interface ICustomer
    {
        string GetCustomerNameById(int id);
    }

    public class Customer : ICustomer
    {
        //ctor
        public Customer(){}

        public string GetCustomerNameById(int id)
        {
            return "Dummy Customer Name";
        }
    }

    public class CustomerFactory
    {
        public static ICustomer GetCustomerData()
        {
            return new Customer();
        }
    }

    public class CustomerBLL
    {
        ICustomer _customer;
        public CustomerBLL()
        {
            _customer = CustomerFactory.GetCustomerData();
        }

        public string GetCustomerNameById(int id)
        {
            return _customer.GetCustomerNameById(id);
        }
    }

    public class Program
    {
        static void Main()
        {
            CustomerBLL customerBLL = new CustomerBLL();
            int customerId = 25;
            string customerName = customerBLL.GetCustomerNameById(customerId);


            Console.WriteLine(customerName);
            Console.ReadKey();
        }
    }

Catatan: Kelas harus bergantung pada abstraksi seperti antarmuka atau kelas abstrak, bukan detail spesifik (implementasi antarmuka).

0
Rejwanul Reja