Memahami injeksi ketergantungan

109

Saya membaca tentang injeksi ketergantungan (DI). Bagi saya, itu adalah hal yang sangat rumit untuk dilakukan, karena saya membaca itu juga merujuk pada inversi kontrol (IOC) dan saya merasa akan ikut dalam perjalanan.

Ini adalah pemahaman saya: Alih-alih membuat model di kelas yang juga mengkonsumsinya, Anda meneruskan (menyuntikkan) model (sudah diisi dengan properti menarik) ke tempat yang diperlukan (ke kelas baru yang bisa menjadikannya sebagai parameter dalam konstruktor).

Bagi saya, ini hanya lewat argumen. Aku pasti rindu mengerti maksudnya? Mungkin menjadi lebih jelas dengan proyek yang lebih besar?

Pemahaman saya adalah non-DI (menggunakan kode semu):

public void Start()
{
    MyClass class = new MyClass();
}

...

public MyClass()
{
    this.MyInterface = new MyInterface(); 
}

Dan DI akan menjadi

public void Start()
{
    MyInterface myInterface = new MyInterface();
    MyClass class = new MyClass(myInterface);
}

...

public MyClass(MyInterface myInterface)
{
    this.MyInterface = myInterface; 
}

Bisakah seseorang memberi penerangan karena saya yakin saya berada dalam kekacauan di sini.

MyDaftQuestions
sumber
5
Cobalah memiliki 5 dari MyInterface dan MyClass DAN memiliki beberapa kelas membentuk rantai ketergantungan. Itu menjadi sakit di pantat untuk mengatur segalanya.
Euforia
159
" Bagiku, ini hanya lewat argumen. Aku pasti salah paham intinya? " Tidak. Kamu mendapatkannya. Itu "injeksi ketergantungan". Sekarang lihat jargon gila apa lagi yang bisa Anda buat untuk konsep sederhana, itu menyenangkan!
Eric Lippert
8
Saya tidak bisa cukup membenarkan komentar Tuan Lippert. Saya juga menganggapnya sebagai jargon yang membingungkan untuk sesuatu yang saya lakukan secara alami.
Alex Humphrey
16
@ EricLippert: Meskipun benar, saya pikir itu sedikit reduktif. Parameter-as-DI bukan konsep langsung langsung untuk rata-rata programer imperatif / OO Anda, yang terbiasa menyampaikan data. DI sini memungkinkan Anda memberikan perilaku , yang membutuhkan pandangan dunia yang lebih fleksibel daripada yang dimiliki oleh seorang programmer biasa-biasa saja.
Phoshi
14
@ Phoshi: Anda membuat poin yang bagus, tetapi Anda juga sudah tahu mengapa saya skeptis bahwa "injeksi ketergantungan" adalah ide yang bagus. Jika saya menulis kelas yang tergantung pada kebenaran dan kinerjanya pada perilaku kelas lain maka hal terakhir yang ingin saya lakukan adalah membuat pengguna kelas bertanggung jawab untuk membangun dan mengkonfigurasi ketergantungan dengan benar! Itu pekerjaan saya . Setiap kali Anda membiarkan konsumen menyuntikkan ketergantungan Anda membuat titik di mana konsumen mungkin melakukan kesalahan.
Eric Lippert

Jawaban:

106

Ya, Anda menyuntikkan dependensi Anda, baik melalui konstruktor atau melalui properti.
Salah satu alasan untuk ini, bukan untuk membebani MyClass dengan rincian tentang bagaimana instance MyInterface perlu dibangun. MyInterface bisa menjadi sesuatu yang memiliki daftar seluruh dependensi dengan sendirinya dan kode MyClass akan menjadi sangat cepat jika Anda instantiate semua dependensi MyInterface di dalam MyClass.

Alasan lain adalah untuk pengujian.
Jika Anda memiliki ketergantungan pada antarmuka pembaca file dan Anda menyuntikkan dependensi ini melalui konstruktor di, katakanlah, ConsumerClass, itu berarti bahwa selama pengujian, Anda dapat melewati implementasi di-memori dari pembaca file ke ConsumerClass, menghindari kebutuhan untuk lakukan I / O selama pengujian.

Stefan Billiet
sumber
13
Ini bagus, dijelaskan dengan baik. Silakan terima +1 imajiner karena perwakilan saya belum mengizinkannya!
MyDaftQuestions
11
Skenario konstruksi yang rumit adalah kandidat yang baik untuk menggunakan pola Pabrik. Dengan begitu, pabrik mengambil tanggung jawab untuk membangun objek, sehingga kelas itu sendiri tidak perlu dan Anda tetap berpegang pada prinsip tanggung jawab tunggal.
Matt Gibson
1
@MattGibson poin bagus.
Stefan Billiet
2
+1 untuk pengujian, DI yang terstruktur dengan baik akan membantu mempercepat pengujian dan membuat unit test terkandung dalam kelas yang Anda uji.
Umur Kontacı
52

Bagaimana DI dapat diimplementasikan sangat tergantung pada bahasa yang digunakan.

Berikut adalah contoh non-DI sederhana:

class Foo {
    private Bar bar;
    private Qux qux;

    public Foo() {
        bar = new Bar();
        qux = new Qux();
    }
}

Ini menyebalkan, misalnya ketika untuk tes saya ingin menggunakan objek tiruan untuk bar. Jadi kita dapat membuat ini lebih fleksibel dan memungkinkan contoh untuk dilewatkan melalui konstruktor:

class Foo {
    private Bar bar;
    private Qux qux;

    public Foo(Bar bar, Qux qux) {
        this.bar = bar;
        this.qux = qux;
    }
}

// in production:
new Foo(new Bar(), new Qux());
// in test:
new Foo(new BarMock(), new Qux());

Ini sudah merupakan bentuk injeksi ketergantungan yang paling sederhana. Tapi ini masih menyebalkan karena semuanya harus dilakukan secara manual (juga, karena penelepon dapat memegang referensi ke objek internal kita dan dengan demikian membatalkan keadaan kita).

Kami dapat memperkenalkan lebih banyak abstraksi dengan menggunakan pabrik:

  • Salah satu opsi adalah Fooagar dihasilkan oleh pabrik abstrak

    interface FooFactory {
        public Foo makeFoo();
    }
    
    class ProductionFooFactory implements FooFactory {
        public Foo makeFoo() { return new Foo(new Bar(), new Baz()) }
    }
    
    class TestFooFactory implements FooFactory {
        public Foo makeFoo() { return new Foo(new BarMock(), new Baz()) }
    }
    
    FooFactory fac = ...; // depends on test or production
    Foo foo = fac.makeFoo();
    
  • Pilihan lain adalah menyerahkan pabrik ke konstruktor:

    interface DependencyManager {
        public Bar makeBar();
        public Qux makeQux();
    }
    class ProductionDM implements DependencyManager {
        public Bar makeBar() { return new Bar() }
        public Qux makeQux() { return new Qux() }
    }
    class TestDM implements DependencyManager {
        public Bar makeBar() { return new BarMock() }
        public Qux makeQux() { return new Qux() }
    }
    
    class Foo {
        private Bar bar;
        private Qux qux;
    
        public Foo(DependencyManager dm) {
            bar = dm.makeBar();
            qux = dm.makeQux();
        }
    }
    

Masalah yang tersisa dengan ini adalah bahwa kita perlu menulis DependencyManagersubkelas baru untuk setiap konfigurasi, dan bahwa jumlah dependensi yang dapat dikelola cukup terbatas (setiap ketergantungan baru membutuhkan metode baru di antarmuka).

Dengan fitur seperti refleksi dan pemuatan kelas dinamis, kita dapat mengelak dari ini. Tetapi ini sangat tergantung pada bahasa yang digunakan. Di Perl, kelas dapat dirujuk dengan nama mereka, dan saya bisa melakukannya

package Foo {
    use signatures;

    sub new($class, $dm) {
        return bless {
            bar => $dm->{bar}->new,
            qux => $dm->{qux}->new,
        } => $class;
    }
}

my $prod = { bar => 'My::Bar', qux => 'My::Qux' };
my $test = { bar => 'BarMock', qux => 'QuxMock' };
$test->{bar} = 'OtherBarMock';  # change conf at runtime

my $foo = Foo->new(rand > 0.5 ? $prod : $test);

Dalam bahasa seperti Java, saya bisa membuat manajer dependensi berperilaku mirip dengan Map<Class, Object>:

Bar bar = dm.make(Bar.class);

Untuk kelas aktual mana yang Bar.classdipecahkan dapat dikonfigurasikan saat runtime, misalnya dengan mempertahankan Map<Class, Class>antarmuka yang akan diimplementasikan.

Map<Class, Class> dependencies = ...;

public <T> T make(Class<T> c) throws ... {
    // plus a lot more error checking...
    return dependencies.get(c).newInstance();
}

Masih ada elemen manual yang terlibat dalam penulisan konstruktor. Tetapi kita dapat membuat konstruktor sama sekali tidak perlu, misalnya dengan menggerakkan DI melalui anotasi:

class Foo {
    @Inject(Bar.class)
    private Bar bar;

    @Inject(Qux.class)
    private Qux qux;

    ...
}

dm.make(Foo.class);  // takes care of initializing "bar" and "qux"

Berikut ini adalah contoh implementasi kerangka kerja DI yang kecil (dan sangat terbatas): http://ideone.com/b2ubuF , meskipun implementasi ini benar-benar tidak dapat digunakan untuk objek yang tidak dapat diubah (implementasi naif ini tidak dapat mengambil parameter apa pun untuk konstruktor).

amon
sumber
7
Ini sangat bagus dengan contoh-contoh yang bagus, walaupun saya perlu beberapa kali membacanya untuk mencerna semuanya, tetapi terima kasih telah meluangkan banyak waktu.
MyDaftQuestions
33
Sangat menyenangkan bagaimana setiap penyederhanaan selanjutnya lebih kompleks
Kromster
48

Teman-teman kami di Stack Overflow memiliki jawaban yang bagus untuk ini . Favorit saya adalah jawaban kedua, termasuk kutipan:

"Dependency Injection" adalah istilah 25 dolar untuk konsep 5 sen. (...) Ketergantungan injeksi berarti memberikan objek variabel instan. (...).

dari blog James Shore . Tentu saja, ada versi / pola yang lebih rumit berlapis-lapis di atas ini, tetapi pada dasarnya cukup untuk memahami apa yang sedang terjadi. Alih-alih objek membuat variabel contoh sendiri, mereka diteruskan dari luar.

pengguna967543
sumber
10
Saya telah membaca ini ... dan sampai sekarang, itu tidak masuk akal ... Sekarang, itu masuk akal. Ketergantungan sebenarnya bukanlah konsep yang terlalu besar untuk dipahami (terlepas dari seberapa kuatnya konsep itu) tetapi ia memiliki nama besar dan banyak kebisingan internet yang terkait dengannya!
MyDaftQuestions
18

Katakanlah Anda sedang melakukan desain interior di ruang tamu Anda: Anda memasang lampu gantung mewah di langit-langit dan memasang lampu lantai yang serasi. Setahun kemudian, istrimu yang cantik memutuskan dia menginginkan ruangan itu sedikit kurang formal. Perlengkapan pencahayaan mana yang lebih mudah diubah?

Gagasan di balik DI adalah menggunakan pendekatan "plug-in" di mana-mana. Ini mungkin tidak terlihat seperti masalah besar jika Anda tinggal di gubuk sederhana tanpa drywall dan semua kabel listrik terbuka. (Anda seorang tukang - Anda dapat mengubah apa pun!) Dan juga, pada aplikasi kecil & sederhana, DI dapat menambah lebih banyak kerumitan daripada nilainya.

Tetapi untuk aplikasi yang besar dan kompleks, DI sangat diperlukan untuk pemeliharaan jangka panjang. Ini memungkinkan Anda untuk "mencabut" seluruh modul dari kode Anda (database backend, misalnya), dan menukar mereka dengan modul yang berbeda yang mencapai hal yang sama tetapi melakukannya dengan cara yang sama sekali berbeda (misalnya sistem penyimpanan cloud).

BTW, diskusi tentang DI tidak akan lengkap tanpa rekomendasi Dependency Injection dalam .NET , oleh Mark Seeman. Jika Anda terbiasa dengan .NET dan Anda pernah ingin terlibat dalam pengembangan SW skala besar, ini adalah bacaan penting. Dia menjelaskan konsepnya jauh lebih baik daripada yang saya bisa.

Biarkan saya meninggalkan Anda dengan satu karakteristik penting terakhir DI yang contoh kode Anda abaikan. DI memungkinkan Anda untuk memanfaatkan potensi fleksibilitas yang sangat besar yang dirangkum dalam pepatah " Program untuk antarmuka ". Untuk sedikit memodifikasi contoh Anda:

public void Main()
{
    ILightFixture fixture = new ClassyChandelier();
    MyRoom room = new MyRoom (fixture);
}
...
public MyRoom(ILightFixture fixture)
{
    this.MyLightFixture = fixture ; 
}

Tapi SEKARANG, karena MyRoomdirancang untuk antarmuka ILightFixture , saya dapat dengan mudah masuk tahun depan dan mengubah satu baris dalam fungsi Utama ("menyuntikkan" ketergantungan "yang berbeda"), dan saya langsung mendapatkan fungsionalitas baru di MyRoom (tanpa harus membangun kembali atau memindahkan MyRoom, jika kebetulan berada di perpustakaan terpisah. Ini tentu saja mengasumsikan bahwa semua implementasi ILightFixture Anda dirancang dengan mempertimbangkan Pergantian Liskov). Semua, hanya dengan perubahan sederhana:

public void Main()
{
    ILightFixture fixture = new MuchLessFormalLightFixture();
    MyRoom room = new MyRoom (fixture);
}

Plus, sekarang saya bisa Unit Uji MyRoom(Anda adalah unit testing, kan?) Dan menggunakan "tiruan" lampu, yang memungkinkan saya untuk menguji MyRoomsepenuhnya independen dariClassyChandelier.

Ada banyak keuntungan, tentu saja, tetapi ini adalah orang-orang yang menjual ide kepada saya.

kmote
sumber
Saya ingin mengucapkan terima kasih, ini sangat membantu saya, tetapi mereka tidak ingin Anda melakukan itu, melainkan menggunakan komentar untuk menyarankan peningkatan, jadi, jika Anda menginginkannya, Anda dapat menambahkan salah satu kelebihan lain yang Anda sebutkan. Tetapi sebaliknya, terima kasih, ini membantu saya.
Steve
2
Saya juga tertarik dengan buku yang Anda referensikan, tetapi saya merasa khawatir bahwa ada buku 584 halaman tentang hal ini.
Steve
4

Ketergantungan injeksi hanya melewati parameter, tetapi itu masih mengarah ke beberapa masalah, dan namanya dimaksudkan untuk sedikit fokus pada mengapa perbedaan antara membuat objek vs melewati satu adalah penting.

Ini penting karena (cukup jelas) setiap objek waktu A memanggil tipe B, A bergantung pada cara B bekerja. Yang berarti bahwa jika A membuat keputusan untuk tipe B beton mana yang akan digunakan, maka banyak fleksibilitas dalam bagaimana A dapat digunakan oleh kelas luar, tanpa memodifikasi A, telah hilang.

Saya menyebutkan ini karena Anda berbicara tentang "kehilangan titik" injeksi ketergantungan, dan ini adalah sebagian besar alasan mengapa orang suka melakukannya. Ini mungkin hanya berarti melewati parameter, tetapi apakah Anda melakukannya dapat menjadi penting.

Juga, beberapa kesulitan dalam mengimplementasikan DI memang terlihat lebih baik di proyek-proyek besar. Anda umumnya akan memindahkan keputusan aktual dari kelas konkret mana yang akan digunakan sampai ke puncak, sehingga metode awal Anda akan terlihat seperti:

public void Start()
{
    MySubSubInterfaceA mySubSubInterfaceA = new mySubSubInterfaceA();
    MySubSubInterfaceB mySubSubInterfaceB = new mySubSubInterfaceB();     
    MySubInterface mySubInterface = new MySubInterface(mySubSubInterfaceA,mySubSubInterfaceB);
    MyInterface myInterface = new MyInterface(MySubInterface);
    MyClass class = new MyClass(myInterface);
}

tetapi dengan 400 baris lain dari jenis kode memukau ini. Jika Anda membayangkan mempertahankannya dari waktu ke waktu daya tarik wadah DI menjadi lebih jelas.

Juga, bayangkan kelas yang, katakanlah, mengimplementasikan IDisposable. Jika kelas-kelas yang menggunakannya mendapatkannya melalui injeksi daripada membuatnya sendiri, bagaimana mereka tahu kapan harus memanggil Buang ()? (Yang merupakan masalah lain yang berhubungan dengan wadah DI.)

Jadi, tentu saja, ketergantungan injeksi hanya melewati parameter, tetapi benar-benar ketika orang mengatakan injeksi ketergantungan mereka berarti "mengirimkan parameter ke objek Anda vs alternatif memiliki objek Anda instantiate sesuatu itu sendiri", dan berurusan dengan semua manfaat dari kelemahan dari pendekatan semacam itu, mungkin dengan bantuan wadah DI.

psr
sumber
Itu banyak subs dan antarmuka! Apakah Anda bermaksud membuat antarmuka baru alih-alih kelas yang mengimplementasikan antarmuka? Tampaknya salah.
JBRWilkinson
@ JBRWilkinson Maksud saya kelas yang mengimplementasikan antarmuka. Saya harus membuatnya lebih jelas. Namun, intinya adalah bahwa aplikasi besar akan memiliki kelas dengan dependensi yang pada gilirannya memiliki dependensi yang pada gilirannya memiliki dependensi ... Mengatur semuanya secara manual dalam metode Utama adalah hasil dari melakukan banyak injeksi konstruktor.
psr
4

Di tingkat kelas, itu mudah.

'Injeksi Ketergantungan' hanya menjawab pertanyaan "bagaimana saya akan menemukan kolaborator saya" dengan "mereka didorong pada Anda - Anda tidak harus pergi dan mendapatkannya sendiri". (Ini mirip dengan - tetapi tidak sama dengan - 'Pembalikan Kontrol' di mana pertanyaan "bagaimana saya akan memesan operasi saya atas input saya?" Memiliki jawaban yang sama).

Satu- satunya manfaat yang mendorong kolaborator Anda pada Anda adalah yang memungkinkan kode klien untuk menggunakan kelas Anda untuk membuat grafik objek yang sesuai dengan kebutuhan saat ini ... Anda belum secara sewenang-wenang menentukan bentuk dan mutabilitas grafik dengan memutuskan secara pribadi jenis konkret dan siklus hidup kolaborator Anda.

(Semua manfaat lainnya, dari testability, lepas kopling, dll, sebagian besar mengikuti dari penggunaan antarmuka dan tidak begitu banyak dari ketergantungan-injeksi-ness, meskipun DI secara alami mempromosikan penggunaan antarmuka).

Perlu dicatat bahwa, jika Anda menghindari instantiate kolaborator Anda sendiri, kelas Anda karenanya harus mendapatkan kolaboratornya dari konstruktor, properti, atau argumen metode (opsi terakhir ini sering diabaikan, omong-omong ... cara itu memang terjadi. tidak selalu masuk akal bagi kelas 'kolaborator untuk menjadi bagian dari' keadaannya ').

Dan itu bagus.

Pada tingkat aplikasi ...

Begitu banyak untuk pandangan per kelas tentang hal-hal. Katakanlah Anda memiliki banyak kelas yang mengikuti aturan "jangan instantiate kolaborator Anda sendiri", dan ingin membuat aplikasi dari mereka. Hal paling sederhana yang harus dilakukan adalah menggunakan kode lama yang baik (alat yang sangat berguna untuk memanggil konstruktor, properti, dan metode!) Untuk menyusun grafik objek yang Anda inginkan, dan memberikan beberapa masukan padanya. (Ya, beberapa objek dalam grafik Anda sendiri akan menjadi pabrik objek, yang telah diedarkan sebagai kolaborator ke objek berumur panjang lainnya dalam grafik, siap untuk digunakan ... Anda tidak dapat melakukan pra-konstruksi setiap objek! ).

... kebutuhan Anda untuk 'secara fleksibel' mengkonfigurasi objek-grafik aplikasi Anda ...

Bergantung pada tujuan Anda yang lain (yang tidak berhubungan dengan kode), Anda mungkin ingin memberi pengguna akhir kontrol atas grafik objek yang digunakan. Ini mengarahkan Anda ke arah skema konfigurasi, dari satu jenis atau lainnya, apakah itu file teks dari desain Anda sendiri dengan beberapa pasangan nama / nilai, file XML, DSL kustom, bahasa deskripsi grafik deklaratif seperti YAML, bahasa skrip imperatif seperti JavaScript, atau sesuatu yang lain yang sesuai dengan tugas yang dihadapi. Apa pun yang diperlukan untuk membuat grafik objek yang valid, dengan cara yang memenuhi kebutuhan pengguna Anda.

... mungkin kekuatan desain yang signifikan.

Dalam keadaan yang paling ekstrem dari tipe itu, Anda dapat memilih untuk mengambil pendekatan yang sangat umum, dan memberi pengguna akhir mekanisme umum untuk 'memasang kabel' grafik objek yang mereka pilih dan bahkan memungkinkan mereka untuk memberikan realisasi konkret dari antarmuka ke runtime! (Dokumentasi Anda adalah permata yang berkilau, pengguna Anda sangat pintar, akrab dengan setidaknya garis besar kasar dari grafik objek aplikasi Anda, tetapi tidak memiliki kompiler yang praktis). Skenario ini secara teoritis dapat terjadi dalam beberapa situasi 'perusahaan'.

Dalam hal ini, Anda mungkin memiliki bahasa deklaratif yang memungkinkan pengguna Anda untuk mengekspresikan jenis apa pun, komposisi grafik objek jenis tersebut, dan palet antarmuka yang dapat dicampur dan dicocokkan oleh pengguna akhir mitos. Untuk mengurangi beban kognitif pada pengguna Anda, Anda lebih suka pendekatan 'konfigurasi dengan konvensi', sehingga mereka hanya perlu melangkah dan menaiki objek-grafik-fragmen minat, daripada bergulat dengan semuanya.

Dasar kau orang miskin!

Karena Anda tidak suka menulis semua itu sendiri (tapi serius, lihat mengikat YAML untuk bahasa Anda), Anda menggunakan semacam kerangka DI.

Bergantung pada kematangan kerangka kerja itu, Anda mungkin tidak memiliki opsi untuk menggunakan konstruktor-injeksi, bahkan ketika itu masuk akal (kolaborator tidak berubah selama masa objek), sehingga memaksa Anda untuk menggunakan Setter Injection (bahkan ketika kolaborator tidak berubah selama masa objek, dan bahkan ketika tidak ada alasan logis mengapa semua implementasi konkret antarmuka harus memiliki kolaborator dari tipe tertentu). Jika demikian, Anda saat ini berada dalam neraka penggabungan yang kuat, meskipun telah rajin 'menggunakan antarmuka' di seluruh basis kode Anda - horor!

Semoga saja, Anda menggunakan kerangka kerja DI yang memberi Anda opsi injeksi konstruktor, dan pengguna Anda hanya sedikit pemarah pada Anda karena tidak menghabiskan lebih banyak waktu untuk memikirkan hal-hal spesifik yang mereka butuhkan untuk mengonfigurasi dan memberi mereka UI yang lebih cocok untuk tugas tersebut. di tangan. (Meskipun untuk bersikap adil, Anda mungkin memang mencoba memikirkan cara, tetapi JavaEE mengecewakan Anda dan Anda harus menggunakan hack yang mengerikan ini).

Bootnote

Pada titik mana pun Anda tidak pernah menggunakan Google Guice, yang memberi Anda pembuat kode cara untuk menghilangkan tugas menyusun objek-grafik dengan kode ... dengan menulis kode. Argh!

David Bullock
sumber
0

Banyak orang mengatakan di sini bahwa DI adalah mekanisme untuk meng-out-source inisialisasi variabel instan.

Untuk contoh yang jelas dan sederhana, Anda tidak benar-benar membutuhkan "injeksi ketergantungan". Anda dapat melakukan ini dengan parameter sederhana yang dikirimkan ke konstruktor, seperti yang Anda catat dalam pertanyaan.

Namun, saya pikir mekanisme "injeksi ketergantungan" khusus berguna ketika Anda berbicara tentang sumber daya tingkat aplikasi, di mana penelepon dan callee tidak tahu apa-apa tentang sumber daya ini (dan tidak ingin tahu!).

Misalnya: Koneksi basis data, Konteks keamanan, Fasilitas logging, File konfigurasi, dll.

Selain itu, secara umum setiap singleton adalah kandidat yang baik untuk DI. Semakin banyak yang Anda miliki dan gunakan, semakin banyak peluang yang Anda butuhkan DI.

Untuk jenis sumber daya ini cukup jelas bahwa tidak masuk akal untuk memindahkan mereka semua melalui daftar parameter dan oleh karena itu DI diciptakan.

Catatan terakhir, Anda juga memerlukan wadah DI, yang akan melakukan injeksi yang sebenarnya ... (sekali lagi, untuk melepaskan penelepon dan meninggalkan detail implementasi).

gamliela
sumber
-2

Jika Anda memasukkan tipe referensi abstrak maka kode panggilan dapat melewati objek apa pun yang memperluas / mengimplementasikan tipe abstrak. Jadi di masa depan kelas yang menggunakan objek bisa menggunakan objek baru yang telah dibuat tanpa pernah memodifikasi kodenya.

DavidB
sumber
-4

Ini adalah pilihan lain selain jawaban amon

Gunakan Pembangun:

kelas Foo {
    bar pribadi;
    Qux qux pribadi;

    Foo pribadi () {
    }

    Foo publik (pembangun pembangun) {
        this.bar = builder.bar;
        this.qux = builder.qux;
    }

    Builder kelas statis publik {
        bar pribadi;
        Qux qux pribadi;
        setBar Buider publik (bilah Bar) {
            this.bar = bar;
            kembalikan ini;
        }
        setQux Builder publik (Qux qux) {
            this.qux = qux;
            kembalikan ini;
        }
        build Foo publik () {
            kembalikan Foo baru (ini);
        }
    }
}

// dalam produksi:
Foo foo = Foo.Builder baru ()
    .setBar (Bar baru ())
    .setQux (Qux baru ())
    .membangun();

// dalam ujian:
Foo testFoo = Foo.Builder baru ()
    .setBar (BarMock baru ())
    .setQux (Qux baru ())
    .membangun();

Ini yang saya gunakan untuk dependensi. Saya tidak menggunakan kerangka kerja DI. Mereka menghalangi.

pengguna3155341
sumber
4
Ini tidak menambah banyak jawaban lain dari tahun lalu, dan tidak memberikan penjelasan mengapa pembangun dapat membantu penanya untuk memahami injeksi ketergantungan.
@Snowman Ini adalah opsi LAIN menggantikan DI. Maaf kamu tidak melihatnya seperti itu. Terima kasih atas suaranya.
user3155341
1
penanya ingin memahami jika DI benar-benar sesederhana melewati parameter, dia tidak meminta alternatif untuk DI.
1
Contoh-contoh OP sangat bagus. Contoh Anda memberikan lebih banyak kerumitan untuk sesuatu yang merupakan ide sederhana. Mereka tidak mengilustrasikan masalah pertanyaan.
Adam Zuckerman
Konstruktor DI melewatkan parameter. Kecuali Anda melewati objek antarmuka daripada objek kelas beton. Ini 'ketergantungan' ini pada implementasi konkret yang diselesaikan melalui DI. Konstruktor tidak tahu tipe yang dilewatkan, juga tidak peduli, ia hanya tahu bahwa ia menerima tipe yang mengimplementasikan antarmuka. Saya sarankan PluralSight, Ini memiliki pemula DI / IOC yang bagus.
Hardgraf