Ada beberapa pertanyaan yang sudah diposting dengan pertanyaan-pertanyaan spesifik tentang dependency injection, seperti kapan harus menggunakannya dan bagaimana kerangka kerja yang ada untuk itu. Namun,
Apa dependency injection dan kapan/mengapa harus atau seharusnya't itu digunakan?
Definisi terbaik saya've ditemukan sejauh ini adalah satu oleh James Shore:
"Dependency Injection" adalah 25 dollar istilah untuk 5-cent konsep. [...] Dependency injection berarti memberikan obyek instance variable. [...].
Ada sebuah artikel oleh Martin Fowler yang mungkin berguna, juga.
Dependency injection pada dasarnya adalah memberikan benda-benda yang merupakan objek kebutuhan (dependensi) daripada harus membangun mereka sendiri. It's teknik yang sangat berguna untuk pengujian, karena memungkinkan dependensi untuk dipermainkan atau dipotong keluar.
Dependensi dapat disuntikkan ke benda-benda dengan berbagai cara (seperti constructor injection atau setter injection). Satu bahkan dapat menggunakan berupa dependency injection kerangka kerja (misalnya musim Semi) untuk melakukan itu, tetapi mereka pasti tidak't diperlukan. Anda don't membutuhkan orang kerangka kerja untuk memiliki dependency injection. Instantiating dan melewati objek (dependensi) secara eksplisit adalah hanya sebagai baik injeksi sebagai injeksi oleh framework.
Dependency Injection lewat ketergantungan lainnya benda atau kerangka( ketergantungan injector).
Dependency injection membuat pengujian lebih mudah. Injeksi dapat dilakukan melalui constructor.
SomeClass()
memiliki konstruktor seperti berikut:
public SomeClass() {
myObject = Factory.getObject();
}
Masalah:
Jika myObject
melibatkan tugas-tugas yang kompleks seperti disk access atau akses jaringan, itu adalah hard untuk melakukan unit test pada SomeClass()
. Programmer harus pura-pura myObject
dan mungkin mencegat pabrik call.
Alternatif solusi:
myObject
sebagai argumen untuk konstruktorpublic SomeClass (MyClass myObject) {
this.myObject = myObject;
}
myObject
dapat dikirimkan secara langsung yang membuat pengujian lebih mudah.
Hal ini lebih sulit untuk mengisolasi komponen-komponen dalam unit pengujian tanpa dependency injection.
Pada tahun 2013, ketika saya menulis jawaban ini, ini adalah tema utama di Google Testing Blog. Tetap keuntungan terbesar untuk saya, karena programmer tidak selalu membutuhkan fleksibilitas ekstra dalam run-time desain (misalnya, untuk layanan locator atau pola yang sama). Programmer sering perlu untuk mengisolasi kelas selama pengujian.
Saya menemukan ini lucu contoh dalam hal loose coupling:
Aplikasi ini terdiri dari banyak objek yang berkolaborasi dengan satu sama lain untuk melakukan beberapa hal yang berguna. Secara tradisional masing-masing objek adalah bertanggung jawab untuk memperoleh sendiri referensi untuk benda-benda yang tergantung (dependensi) itu berkolaborasi dengan. Hal ini menyebabkan sangat digabungkan kelas dan keras-untuk-menguji kode.
Sebagai contoh, pertimbangkan sebuah Mobil
objek.
Mobil
tergantung pada roda, mesin, bahan bakar, baterai, dll. untuk menjalankan. Secara tradisional kita mendefinisikan merek tersebut tergantung objek bersama dengan definisi Mobil
objek.
Tanpa Dependency Injection (DI):
class Car{
private Wheel wh = new NepaliRubberWheel();
private Battery bt = new ExcideBattery();
//The rest
}
Di sini, Mobil
objek bertanggung jawab untuk menciptakan ketergantungan objek.
Bagaimana jika kita ingin mengubah jenis tergantung objek - mengatakan Roda
- setelah awal NepaliRubberWheel()
tusukan?
Kita perlu menciptakan objek Mobil dengan yang baru ketergantungan mengatakan ChineseRubberWheel()
, tapi hanya Mobil
produsen dapat melakukan itu.
Lalu apa Dependency Injection
lakukan untuk kita...?
Ketika menggunakan dependency injection, benda-benda yang diberikan mereka dependensi pada jangka waktu lebih dari waktu kompilasi (manufaktur mobil waktu).
Sehingga kita sekarang dapat mengubah Roda
setiap kali kita inginkan. Di sini, ketergantungan
(roda
) dapat disuntikkan ke Mobil
pada waktu berjalan.
Setelah menggunakan dependency injection:
Di sini, kita suntikan the ketergantungan (Roda dan Baterai) pada saat runtime. Oleh karena itu istilah : Dependency Injection.
class Car{
private Wheel wh; // Inject an Instance of Wheel (dependency of car) at runtime
private Battery bt; // Inject an Instance of Battery (dependency of car) at runtime
Car(Wheel wh,Battery bt) {
this.wh = wh;
this.bt = bt;
}
//Or we can have setters
void setWheel(Wheel wh) {
this.wh = wh;
}
}
Sumber: Pemahaman dependency injection
Dependency Injection adalah sebuah praktek di mana benda-benda yang dirancang dalam cara di mana mereka menerima contoh benda-benda dari potongan-potongan lain dari kode, alih-alih membangun mereka secara internal. Ini berarti bahwa setiap objek yang mengimplementasikan interface yang diperlukan oleh objek dapat diganti dalam tanpa mengubah kode, yang menyederhanakan pengujian, dan meningkatkan decoupling.
Misalnya, pertimbangkan kelas pada:
public class PersonService {
public void addManager( Person employee, Person newManager ) { ... }
public void removeManager( Person employee, Person oldManager ) { ... }
public Group getGroupByManager( Person manager ) { ... }
}
public class GroupMembershipService() {
public void addPersonToGroup( Person person, Group group ) { ... }
public void removePersonFromGroup( Person person, Group group ) { ... }
}
Dalam contoh ini, pelaksanaan PersonService::addManager
dan PersonService::removeManager
akan membutuhkan sebuah contoh dari GroupMembershipService
dalam rangka untuk melakukan tugasnya. Tanpa Injeksi Ketergantungan, cara tradisional melakukan hal ini akan instantiate baru GroupMembershipService
dalam konstruktor dari PersonService
dan menggunakan contoh atribut dalam kedua fungsi. Namun, jika konstruktor GroupMembershipService
memiliki beberapa hal-hal yang memerlukan, atau lebih buruk lagi, ada beberapa inisialisasi "setter" yang harus disebut di GroupMembershipService
, kode tumbuh lebih cepat, dan PersonService
sekarang tidak hanya tergantung pada GroupMembershipService
tetapi juga segala sesuatu yang lain yang GroupMembershipService
tergantung pada. Selain itu, hubungan dengan GroupMembershipService
hardcoded ke PersonService
yang berarti bahwa anda dapat't "dummy up" GroupMembershipService
untuk tujuan pengujian, atau untuk menggunakan strategi pola di bagian yang berbeda dari aplikasi anda.
Dengan Dependency Injection, bukan instantiating yang GroupMembershipService
dalam PersonService
, anda'd baik menyebarkannya ke PersonService
konstruktor, atau yang lain menambahkan Properti (getter dan setter) untuk menetapkan contoh lokal itu. Ini berarti bahwa anda PersonService
tidak lagi harus khawatir tentang bagaimana untuk membuat GroupMembershipService
, hanya menerima orang-orang itu's diberikan, dan bekerja dengan mereka. Ini juga berarti bahwa apa pun yang merupakan subclass dari GroupMembershipService
, atau mengimplementasikan GroupMembershipService
antarmuka dapat "disuntikkan" ke PersonService
, dan PersonService
doesn't perlu tahu tentang perubahan.
Jawaban yang diterima adalah salah satu yang baik - tapi saya ingin tambahkan ke ini bahwa DI sangat banyak seperti klasik menghindari dari hardcoded konstanta di dalam kode.
Ketika anda menggunakan beberapa konstanta seperti nama database anda'a dengan cepat bergerak dari dalam kode untuk beberapa file konfigurasi dan melewati sebuah variabel yang berisi nilai tersebut ke tempat di mana diperlukan. Alasan untuk melakukan itu adalah bahwa konstanta ini biasanya perubahan lebih sering daripada sisa dari kode. Misalnya jika anda'd ingin menguji kode dalam database test.
DI analog dengan ini dalam dunia pemrograman Berorientasi Objek. Nilai-nilai yang ada bukan konstanta literal adalah seluruh benda - tetapi alasan untuk memindahkan kode membuat mereka keluar dari kelas kode yang mirip - benda perubahan lebih sering maka kode yang menggunakan mereka. Salah satu kasus penting yang mana perubahan tersebut diperlukan adalah tes.
Let's coba contoh sederhana dengan Mobil dan Mesin kelas-kelas, setiap mobil memerlukan mesin untuk pergi ke mana saja, setidaknya untuk saat ini. Jadi di bawah ini bagaimana kode akan terlihat tanpa dependency injection.
public class Car
{
public Car()
{
GasEngine engine = new GasEngine();
engine.Start();
}
}
public class GasEngine
{
public void Start()
{
Console.WriteLine("I use gas as my fuel!");
}
}
Dan untuk instantiate kelas Mobil yang akan kita gunakan kode:
Car car = new Car();
Masalah dengan kode ini yang kita erat digabungkan ke GasEngine dan jika kami memutuskan untuk mengubah itu untuk ElectricityEngine maka kita akan perlu untuk menulis ulang kelas Mobil. Dan semakin besar aplikasi yang lebih banyak masalah dan sakit kepala, kita akan memiliki untuk menambah dan menggunakan jenis baru mesin.
Dengan kata lain dengan pendekatan ini adalah bahwa tingkat tinggi Mobil kelas ini tergantung pada tingkat yang lebih rendah GasEngine kelas yang melanggar Dependency Inversion Principle(DIP) dari PADAT. DIP menunjukkan bahwa kita harus bergantung pada abstraksi, bukan beton kelas. Sehingga untuk memenuhi kebutuhan ini kami memperkenalkan IEngine antarmuka dan menulis ulang kode seperti di bawah ini:
public interface IEngine
{
void Start();
}
public class GasEngine : IEngine
{
public void Start()
{
Console.WriteLine("I use gas as my fuel!");
}
}
public class ElectricityEngine : IEngine
{
public void Start()
{
Console.WriteLine("I am electrocar");
}
}
public class Car
{
private readonly IEngine _engine;
public Car(IEngine engine)
{
_engine = engine;
}
public void Run()
{
_engine.Start();
}
}
Sekarang kami kelas Mobil adalah tergantung pada hanya IEngine antarmuka, tidak spesifik implementasi dari mesin. Sekarang, satu-satunya caranya adalah bagaimana kita membuat sebuah instance dari Mobil dan memberikan hal yang sebenarnya beton Mesin kelas seperti GasEngine atau ElectricityEngine. Yang's dimana Dependency Injection datang dalam.
Car gasCar = new Car(new GasEngine());
gasCar.Run();
Car electroCar = new Car(new ElectricityEngine());
electroCar.Run();
Di sini kami pada dasarnya inject(lulus) ketergantungan kita(contoh Mesin) untuk Mobil konstruktor. Jadi sekarang kami kelas loose coupling antara benda-benda dan dependensi mereka, dan kita dapat dengan mudah menambahkan jenis baru mesin tanpa mengubah kelas Mobil.
Manfaat utama dari Dependency Injection bahwa kelas yang lebih longgar ditambah, karena mereka tidak memiliki hard-coded dependensi. Berikut ini Ketergantungan Inversi Prinsip yang telah disebutkan di atas. Bukan referensi spesifik implementasi, kelas permintaan abstraksi (biasanya antarmuka) yang diberikan kepada mereka ketika kelas dibangun.
Sehingga pada akhirnya Dependency injection adalah teknik untuk mencapai loose coupling antara benda-benda dan dependensi mereka. Bukan langsung instantiating dependensi yang diperlukan kelas di untuk melakukan tindakan tersebut, dependensi yang disediakan untuk kelas (paling sering) melalui constructor injection.
Juga ketika kita memiliki banyak dependensi itu adalah praktek yang sangat baik untuk penggunaan Inversion of Control(IoC) wadah yang kita dapat membedakan mana antarmuka harus dipetakan ke yang konkrit implementasi untuk semua ketergantungan kita dan kita dapat memiliki itu mengatasi ketergantungan tersebut untuk kita ketika membangun objek kita. Misalnya, kita bisa menentukan dalam pemetaan untuk IoC container bahwa IEngine ketergantungan harus dipetakan ke GasEngine kelas dan ketika kita meminta IoC container untuk sebuah instance dari kami Mobil kelas, maka secara otomatis akan membangun kami Mobil kelas dengan GasEngine ketergantungan berlalu dalam.
UPDATE: Menyaksikan saja tentang EF Inti dari Julie Lerman baru-baru ini dan juga menyukainya definisi singkat tentang DI.
Dependency injection adalah sebuah pola untuk memungkinkan aplikasi untuk inject benda-benda on the fly untuk kelas yang membutuhkannya, tanpa memaksa mereka kelas bertanggung jawab untuk objek tersebut. Hal ini memungkinkan kode anda untuk menjadi lebih longgar digabungkan, dan Entity Framework colokan Inti dalam yang sama sistem pelayanan.
Let's bayangkan bahwa anda ingin pergi memancing:
Tanpa ketergantungan injeksi, anda perlu untuk mengurus semuanya sendiri. Anda perlu untuk menemukan sebuah perahu, untuk membeli pancing, untuk mencari umpan, dll. It's mungkin, tentu saja, tapi itu menempatkan banyak tanggung jawab pada anda. Dalam ketentuan perangkat lunak, itu berarti bahwa anda telah melakukan pencarian untuk semua hal-hal ini.
Dengan dependency injection, ada orang lain yang mengurus semua persiapan dan peralatan yang diperlukan tersedia untuk anda. Anda akan menerima ("yang akan disuntikkan") perahu, pancing dan umpan - semua siap untuk digunakan.
Ini adalah yang paling sederhana penjelasan tentang Dependency Injection dan Dependency Injection Wadah yang pernah saya lihat:
Dependency Injection dan dependency Injection Wadah adalah hal yang berbeda:
Anda don't perlu wadah untuk melakukan dependency injection. Namun sebuah wadah yang dapat membantu anda.
Doesn't "dependency injection" hanya berarti menggunakan parameter konstruktor dan publik setter?
James Pantai's artikel di bawah ini menunjukkan contoh-contoh berikut untuk perbandingan.
Constructor tanpa ketergantungan injeksi:
public class Example {
private DatabaseThingie myDatabase;
public Example() {
myDatabase = new DatabaseThingie();
}
public void doStuff() {
...
myDatabase.getData();
...
}
}
Konstruktor dengan dependency injection:
public class Example {
private DatabaseThingie myDatabase;
public Example(DatabaseThingie useThisDatabaseInstead) {
myDatabase = useThisDatabaseInstead;
}
public void doStuff() {
...
myDatabase.getData();
...
}
}
Untuk membuat Dependency Injection konsep sederhana untuk memahami. Let's mengambil contoh dari tombol switch untuk mengaktifkan(on/off) lampu.
Beralih perlu tahu terlebih dahulu dimana bola saya terhubung ke (hard-coded ketergantungan). Jadi,
Switch -> PermanentBulb //switch terhubung langsung dengan permanen bulb, pengujian tidak mungkin dengan mudah
Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}
Switch hanya tahu saya perlu untuk mengaktifkan/menonaktifkan mana Bola akan diteruskan ke saya. Jadi,
Switch -> Bulb1 ATAU Bohlam2 ATAU NightBulb (disuntikkan ketergantungan)
Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}
Memodifikasi James Contoh untuk Saklar dan Lampu:
public class SwitchTest {
TestToggleBulb() {
MockBulb mockbulb = new MockBulb();
// MockBulb is a subclass of Bulb, so we can
// "inject" it here:
Switch switch = new Switch(mockBulb);
switch.ToggleBulb();
mockBulb.AssertToggleWasCalled();
}
}
public class Switch {
private Bulb myBulb;
public Switch() {
myBulb = new Bulb();
}
public Switch(Bulb useThisBulbInstead) {
myBulb = useThisBulbInstead;
}
public void ToggleBulb() {
...
myBulb.Toggle();
...
}
}`
Apa Dependency Injection (DI)?
Seperti yang dikatakan orang lain, Dependency Injection(DI) menghilangkan tanggung jawab dari penciptaan langsung, dan pengelolaan umur, dari benda lain contoh di atas yang kami kelas bunga (konsumen kelas) tergantung (dalam UML rasa). Hal ini malah berlalu ke kelas konsumen, biasanya sebagai parameter konstruktor atau melalui properti setter (pengelolaan objek ketergantungan proses dan melewati untuk konsumen kelas ini biasanya dilakukan oleh Inversion of Control (IoC) wadah, tetapi yang's topik yang lain).
DI, DIP dan SOLID
Secara khusus, dalam paradigma Robert C Martin's prinsip-prinsip yang SOLID Object Oriented Desain, DI
adalah salah satu kemungkinan implementasi dari Dependency Inversion Principle (DIP). The DIP adalah D
dari SOLID
mantra - lain-lain DIP implementasi mencakup Layanan Locator, dan Plugin pola.
Tujuan dari DIP adalah untuk memisahkan ketat, beton dependensi antara kelas-kelas, dan sebaliknya, untuk melonggarkan kopling dengan cara abstraksi, yang dapat dicapai melalui sebuah interface
, kelas abstrak
atau pure virtual class
, tergantung pada bahasa dan pendekatan yang digunakan.
Tanpa DIP, kode kami (saya've ini disebut 'mengkonsumsi kelas') ini langsung digabungkan ke beton ketergantungan dan juga sering dibebani dengan tanggung jawab untuk mengetahui cara memperoleh, dan mengelola, contoh dari ketergantungan ini, yaitu secara konseptual:
"I need to create/use a Foo and invoke method `GetBar()`"
Sedangkan setelah penerapan DIP, persyaratan kendor, dan kepedulian memperoleh dan mengelola umur Foo
ketergantungan telah dihapus:
"I need to invoke something which offers `GetBar()`"
Mengapa menggunakan DIP (dan DI)? Decoupling dependensi antara kelas-kelas dalam cara ini memungkinkan untuk mudah substitusi ini ketergantungan kelas dengan implementasi lainnya yang juga memenuhi prasyarat dari abstraksi (misalnya ketergantungan dapat diaktifkan dengan yang lain implementasi dari antarmuka yang sama). Selain itu, sebagai orang lain telah disebutkan, mungkin yang alasan yang paling umum untuk memisahkan kelas melalui DIP adalah untuk memungkinkan mengkonsumsi kelas yang akan diuji dalam isolasi, karena ini sama dependensi sekarang dapat mematikan dan/atau diejek. Salah satu konsekuensi dari DI adalah bahwa umur manajemen dari ketergantungan instance objek tidak lagi dikendalikan oleh mengkonsumsi kelas, sebagai objek ketergantungan sekarang diteruskan ke dalam mengkonsumsi kelas (melalui constructor atau setter injection). Hal ini dapat dilihat dalam cara yang berbeda:
Membuat
di pabrik yang diperlukan, dan membuang hal ini setelah selesai. MyDepClass
adalah benang yang aman - bagaimana jika kita membuat sebuah singleton dan inject contoh yang sama ke semua konsumen?)
Contoh
Berikut ini's sederhana C# pelaksanaan. Diberikan di bawah ini Memakan kelas: public class MyLogger
{
public void LogRecord(string somethingToLog)
{
Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
}
}
Meskipun tampaknya tidak berbahaya, ia memiliki dua statis
ketergantungan pada dua kelas lainnya, Sistem.DateTime
dan Sistem.Console
, yang tidak hanya membatasi penebangan pilihan output (logging untuk konsol akan sia-sia jika tidak ada yang menonton), tapi lebih buruk, sulit untuk secara otomatis menguji mengingat ketergantungan pada non-deterministic system clock.
Namun kita bisa menerapkan DIP
untuk kelas ini, dengan mengabstraksi keluar kepedulian timestamping sebagai ketergantungan, dan coupling MyLogger
hanya untuk sebuah antarmuka yang sederhana:
public interface IClock
{
DateTime Now { get; }
}
Kami juga dapat melonggarkan ketergantungan pada Konsol
untuk sebuah abstraksi, seperti TextWriter
. Ketergantungan Injeksi ini biasanya diimplementasikan sebagai pembina
injeksi (melewati sebuah abstraksi untuk ketergantungan sebagai parameter untuk konstruktor consuming class) atau Setter Injection
(melewati ketergantungan melalui setXyz()
setter atau .Net Properti dengan {set;}
didefinisikan). Constructor Injection lebih disukai, karena ini adalah kelas akan dalam keadaan yang benar setelah konstruksi, dan memungkinkan internal ketergantungan bidang yang akan ditandai sebagai readonly
(C#) atau final
(Jawa). Jadi menggunakan constructor injection pada contoh di atas, ini meninggalkan kita dengan:
public class MyLogger : ILogger // Others will depend on our logger.
{
private readonly TextWriter _output;
private readonly IClock _clock;
// Dependencies are injected through the constructor
public MyLogger(TextWriter stream, IClock clock)
{
_output = stream;
_clock = clock;
}
public void LogRecord(string somethingToLog)
{
// We can now use our dependencies through the abstraction
// and without knowledge of the lifespans of the dependencies
_output.Write("{0:yyyy-MM-dd HH:mm:ss} - {1}", _clock.Now, somethingToLog);
}
}
(Beton Jam
perlu disediakan, yang tentu saja bisa kembali ke DateTime.Sekarang
, dan dua dependensi yang harus disediakan oleh IoC container melalui constructor injection)
Otomatis Unit Tes yang dapat dibangun, yang pasti membuktikan bahwa kami logger bekerja dengan benar, karena kita sekarang memiliki kontrol atas dependensi - waktu, dan kita bisa memata-matai tertulis output:
[Test]
public void LoggingMustRecordAllInformationAndStampTheTime()
{
// Arrange
var mockClock = new Mock<IClock>();
mockClock.Setup(c => c.Now).Returns(new DateTime(2015, 4, 11, 12, 31, 45));
var fakeConsole = new StringWriter();
// Act
new MyLogger(fakeConsole, mockClock.Object)
.LogRecord("Foo");
// Assert
Assert.AreEqual("2015-04-11 12:31:45 - Foo", fakeConsole.ToString());
}
Langkah-Langkah Berikutnya
Dependency injection ini selalu dikaitkan dengan Inversi Kontrol kontainer(IoC), untuk inject (memberikan) beton ketergantungan kasus, dan untuk mengelola umur kasus. Selama konfigurasi / bootstrap proses, IoC
wadah mengizinkan berikut didefinisikan:
IBar
, kembali ConcreteBar
contoh") IDisposable
dan akan mengambil tanggung jawab untuk Membuang
dependensi sejalan dengan dikonfigurasi umur manajemen.
Biasanya, setelah IoC wadah yang telah dikonfigurasi / dinyalakan, mereka beroperasi dengan mulus di latar belakang yang memungkinkan programmer untuk fokus pada kode di tangan daripada mengkhawatirkan tentang dependensi. kunci untuk DI-ramah kode ini untuk menghindari statis coupling dari kelas, dan tidak untuk penggunaan baru() untuk penciptaan Ketergantungan Sesuai contoh di atas, decoupling dari dependensi tidak memerlukan beberapa usaha desain, dan untuk pengembang, ada pergeseran paradigma yang dibutuhkan untuk mematahkan kebiasaan
baru a'ing dependensi secara langsung, dan bukannya percaya wadah untuk mengelola dependensi. Tapi manfaatnya sangat banyak, terutama dalam kemampuan untuk benar-benar menguji kelas dari bunga. **Catatan** : penciptaan / pemetaan / proyeksi (via
baru ..()) dari POCO / POJO / Serialisasi DTOs / Entitas Grafik / Anonim JSON proyeksi et al, yaitu "Data hanya" kelas atau catatan - digunakan atau kembali dari metode ini adalah *tidak* dianggap sebagai Dependensi (dalam UML akal) dan tidak dikenakan DI. Menggunakan
baru` untuk proyek ini adalah baik-baik saja.
Seluruh titik Dependency Injection (DI) adalah untuk menjaga aplikasi source code bersih dan stabil:
Praktis, setiap desain pola memisahkan kekhawatiran untuk membuat perubahan di masa depan mempengaruhi minimum file.
Khusus domain DI-delegasi dari ketergantungan konfigurasi dan inisialisasi.
Jika anda kadang-kadang bekerja di luar Jawa, mengingat bagaimana sumber
lebih sering digunakan dalam banyak bahasa scripting (Shell, Tcl, dll., atau bahkan impor
di Python disalahgunakan untuk tujuan ini).
Mempertimbangkan sederhana dependent.sh
script:
#!/bin/sh
# Dependent
touch "one.txt" "two.txt"
archive_files "one.txt" "two.txt"
Script ini tergantung: tidak't berhasil mengeksekusi sendiri (archive_files
tidak didefinisikan).
Anda mendefinisikan archive_files
di archive_files_zip.sh
implementasi script (menggunakan zip
dalam kasus ini):
#!/bin/sh
# Dependency
function archive_files {
zip files.zip "$@"
}
Bukan sumber
-ing implementasi script langsung di dependen satu, anda menggunakan injector.sh
"wadah" yang membungkus kedua "komponen":
#!/bin/sh
# Injector
source ./archive_files_zip.sh
source ./dependent.sh
The archive_files
ketergantungan telah disuntikkan ke tergantung script.
Anda bisa disuntikkan ketergantungan yang mengimplementasikan archive_files
menggunakan tar
atau xz
.
Jika dependent.sh
script yang digunakan dependensi secara langsung, pendekatan yang akan disebut ketergantungan lookup (yang berlawanan dengan dependency injection):
#!/bin/sh
# Dependent
# dependency look-up
source ./archive_files_zip.sh
touch "one.txt" "two.txt"
archive_files "one.txt" "two.txt"
Sekarang masalahnya adalah bahwa tergantung "komponen" harus melakukan inisialisasi itu sendiri.
"komponen",'s source code lebih baik bersih atau stabil karena setiap perubahan dalam inisialisasi membutuhkan dependensi baru rilis untuk "komponen",'s file kode sumber juga.
DI tidak seperti sebagian besar ditekankan dan dipopulerkan seperti di Jawa frameworks.
Tapi itu's pendekatan generik untuk membagi keprihatinan:
Menggunakan konfigurasi hanya dengan dependency lookup tidak membantu karena jumlah parameter konfigurasi dapat berubah per ketergantungan (misalnya baru jenis otentikasi) serta jumlah yang didukung jenis dependensi (misalnya database baru type).
Semua di atas jawaban yang baik, tujuan saya adalah untuk menjelaskan konsep dengan cara yang sederhana sehingga siapa pun tanpa pengetahuan pemrograman juga dapat memahami konsep
Dependency injection adalah salah satu desain pola yang membantu kita untuk membuat sistem yang kompleks secara lebih sederhana.
Kita dapat melihat berbagai macam aplikasi dari pola ini dalam kehidupan kita sehari hari. Beberapa contoh adalah Tape recorder, VCD, CD Drive, dll.
Gambar di atas adalah gambar Reel-to-reel portable tape recorder, pertengahan abad ke-20. Sumber.
Maksud utama dari sebuah tape recorder untuk merekam atau reproduksi suara.
Saat merancang sebuah sistem itu memerlukan reel untuk merekam atau reproduksi suara atau musik. Ada dua kemungkinan untuk merancang sistem ini
Jika kita menggunakan yang pertama kita perlu membuka mesin untuk mengubah reel. jika kita memilih yang kedua, itu adalah menempatkan kail untuk reel, kita mendapatkan manfaat tambahan dari bermain musik apapun dengan mengubah reel. dan juga mengurangi fungsi hanya untuk bermain apapun dalam reel.
Seperti bijaksana dependency injection adalah proses menghadapi dependensi untuk fokus hanya pada fungsi tertentu dari komponen sehingga komponen dapat digabungkan bersama-sama untuk membentuk suatu sistem yang kompleks.
Manfaat utama kita capai dengan menggunakan dependency injection.
Sekarang hari ini konsep bentuk-bentuk dasar yang dikenal kerangka dalam dunia pemrograman. Musim Semi Sudut dll yang terkenal perangkat lunak kerangka kerja yang dibangun di atas konsep ini
Dependency injection adalah pola yang digunakan untuk membuat contoh dari benda-benda yang benda-benda lain yang mengandalkan tanpa mengetahui pada waktu kompilasi kelas mana yang akan digunakan untuk memberikan fungsi yang atau hanya cara menyuntikkan sifat suatu benda disebut dependency injection.
Contoh untuk Dependency injection
Sebelumnya kita menulis kode seperti ini
Public MyClass{
DependentClass dependentObject
/*
At somewhere in our code we need to instantiate
the object with new operator inorder to use it or perform some method.
*/
dependentObject= new DependentClass();
dependentObject.someMethod();
}
Dengan injeksi Ketergantungan, ketergantungan injector akan melepas instansiasi untuk kita
Public MyClass{
/* Dependency injector will instantiate object*/
DependentClass dependentObject
/*
At somewhere in our code we perform some method.
The process of instantiation will be handled by the dependency injector
*/
dependentObject.someMethod();
}
Anda juga dapat membaca
Dependency Injection(DI) berarti untuk memisahkan benda-benda yang tergantung satu sama lain. Mengatakan objek tergantung pada Objek B jadi idenya adalah untuk memisahkan objek ini dari satu sama lain. Kita tidak perlu keras kode objek menggunakan kata kunci baru agak berbagi dependensi untuk objek pada saat runtime terlepas dari waktu kompilasi. Jika kita berbicara tentang
Kita tidak perlu keras kode objek menggunakan kata kunci baru agak mendefinisikan bean ketergantungan dalam file konfigurasi. Musim semi kontainer akan bertanggung jawab untuk mengaitkan semua.
IOC adalah konsep umum dan dapat dinyatakan dalam banyak cara yang berbeda dan Dependency Injection adalah salah satu contoh konkret dari IOC.
Konstruktor berbasis DI dicapai ketika wadah memanggil konstruktor kelas dengan sejumlah argumen, masing-masing mewakili ketergantungan pada kelas yang lain.
public class Triangle {
private String type;
public String getType(){
return type;
}
public Triangle(String type){ //constructor injection
this.type=type;
}
}
<bean id=triangle" class ="com.test.dependencyInjection.Triangle">
<constructor-arg value="20"/>
</bean>
Setter berbasis DI dicapai oleh wadah memanggil penyetel pada biji setelah menyerukan tidak ada argumen konstruktor atau tidak-argumen statis metode pabrik untuk instantiate anda kacang.
public class Triangle{
private String type;
public String getType(){
return type;
}
public void setType(String type){ //setter injection
this.type = type;
}
}
<!-- setter injection -->
<bean id="triangle" class="com.test.dependencyInjection.Triangle">
<property name="type" value="equivialteral"/>
CATATAN: Itu adalah aturan yang baik untuk menggunakan konstruktor argumen untuk wajib dependensi dan setter untuk opsional dependensi. Perhatikan bahwa jika kita menggunakan penjelasan berdasarkan dari @Diperlukan penjelasan pada setter dapat digunakan untuk membuat setter sebagai dependensi yang diperlukan.
Contoh, kita memiliki 2 kelas Klien
dan Layanan
. Klien
akan menggunakan Jasa
public class Service {
public void doSomeThingInService() {
// ...
}
}
Cara 1)
public class Client {
public void doSomeThingInClient() {
Service service = new Service();
service.doSomeThingInService();
}
}
Cara 2)
public class Client {
Service service = new Service();
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
Cara 3)
public class Client {
Service service;
public Client() {
service = new Service();
}
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
1) 2) 3) Menggunakan
Client client = new Client();
client.doSomeThingInService();
Kelebihan
Kekurangan
Klien
kelasLayanan
konstruktor, kita perlu mengubah kode di semua tempat membuat Layanan
objekCara 1) Constructor injection
public class Client {
Service service;
Client(Service service) {
this.service = service;
}
// Example Client has 2 dependency
// Client(Service service, IDatabas database) {
// this.service = service;
// this.database = database;
// }
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
Menggunakan
Client client = new Client(new Service());
// Client client = new Client(new Service(), new SqliteDatabase());
client.doSomeThingInClient();
Cara 2) Setter injection
public class Client {
Service service;
public void setService(Service service) {
this.service = service;
}
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
Menggunakan
Client client = new Client();
client.setService(new Service());
client.doSomeThingInClient();
Cara 3) Antarmuka injeksi
Cek https://en.wikipedia.org/wiki/Dependency_injection
===
Sekarang, kode ini sudah mengikuti Dependency Injection
dan itu adalah lebih mudah untuk tes Klien
kelas.
Namun, kami masih menggunakan Layanan baru()
banyak waktu dan hal ini tidak baik ketika perubahan Layanan
konstruktor. Untuk mencegah hal itu, kita dapat menggunakan DI injector seperti
Injeksi
public class Injector {
public static Service provideService(){
return new Service();
}
public static IDatabase provideDatatBase(){
return new SqliteDatabase();
}
public static ObjectA provideObjectA(){
return new ObjectA(provideService(...));
}
}
Menggunakan
Service service = Injector.provideService();
Kelebihan
Service
, anda hanya perlu mengubah dalam Injector kelasConstructor Injection
, ketika anda melihat konstruktor dari Klien
, anda akan melihat berapa banyak ketergantungan Klien
kelasKekurangan
Constructor Injection
, Service
objek dibuat ketika Klien
yang dibuat, kapan-kapan kita menggunakan fungsi Klien
kelas tanpa menggunakan Jasa
sehingga menciptakan Layanan
yang terbuanghttps://en.wikipedia.org/wiki/Dependency_injection
ketergantungan adalah suatu objek yang dapat digunakan (
Layanan
) suntikan lewat sebuah ketergantungan (Layanan
) untuk objek tergantung (Klien
) yang akan menggunakannya
Analogi terbaik yang bisa saya pikirkan adalah ahli bedah dan asisten(s) dalam sebuah operasi teater, di mana ahli bedah adalah orang utama dan asistennya yang menyediakan berbagai bedah komponen ketika ia membutuhkannya sehingga ahli bedah dapat berkonsentrasi pada satu hal yang dia lakukan yang terbaik (operasi). Tanpa asisten dokter bedah telah mendapatkan komponen-komponen pada dirinya sendiri setiap kali ia membutuhkan satu.
DI untuk jangka pendek, adalah suatu teknik untuk memindahkan umum tambahan tanggung jawab (beban) pada komponen-komponen untuk mengambil tergantung komponen, dengan menyediakan mereka untuk itu.
DI setiap membawa anda lebih dekat Satu Responsibility (SR) prinsip, seperti bedah yang dapat berkonsentrasi pada operasi
.
Ketika digunakan DI : saya akan merekomendasikan menggunakan DI dalam hampir semua proyek produksi ( kecil/besar), khususnya di lingkungan bisnis yang berubah :)
Mengapa : Karena anda ingin kode anda untuk dapat dengan mudah diuji, mockable dll sehingga anda dapat dengan cepat menguji perubahan dan mendorong ke pasar. Selain itu, mengapa anda tidak ketika anda ada banyak gratis yang mengagumkan tools/framework untuk mendukung anda dalam perjalanan anda ke basis kode di mana anda memiliki kontrol lebih.
Itu berarti bahwa benda-benda hanya harus memiliki banyak dependensi seperti yang diperlukan untuk melakukan pekerjaan mereka dan dependensi harus beberapa. Selain itu, obyek ketergantungan yang harus di antarmuka dan bukan pada "beton" benda-benda, bila mungkin. (Beton objek adalah objek yang dibuat dengan keyword new.) Loose coupling mempromosikan lebih besar usabilitas, mudah pemeliharaan, dan memungkinkan anda untuk dengan mudah memberikan "pura-pura" benda-benda di tempat mahal.
"Ketergantungan" Injeksi (DI) juga dikenal sebagai "Inversion of Control" (IoC), dapat digunakan sebagai teknik untuk mendorong ini kopling longgar.
Ada dua pendekatan utama untuk menerapkan DI:
Ini adalah teknik yang lewat benda-benda dependensi untuk konstruktor.
Perhatikan bahwa constructor menerima sebuah antarmuka dan bukan benda konkret. Juga, perhatikan bahwa pengecualian dilemparkan jika orderDao parameter adalah null. Ini menekankan pentingnya menerima valid ketergantungan. Constructor Injection adalah, dalam pendapat saya, pilihan mekanisme untuk memberikan sebuah objek dependensi. Hal ini jelas untuk pengembang saat memanggil objek yang dependensi perlu diberikan kepada "Orang" objek eksekusi yang tepat.
Tapi perhatikan contoh berikut... Misalkan anda memiliki sebuah kelas dengan sepuluh metode yang tidak memiliki ketergantungan, tetapi anda menambahkan metode baru yang tidak memiliki ketergantungan pada IDAO. Anda bisa mengubah constructor untuk menggunakan Constructor Injection, tapi ini mungkin memaksa anda untuk perubahan untuk semua konstruktor panggilan di semua tempat. Atau, anda hanya bisa menambahkan konstruktor yang mengambil ketergantungan, tapi kemudian bagaimana seorang pengembang dengan mudah mengetahui kapan harus menggunakan satu konstruktor lebih dari yang lain. Akhirnya, jika ketergantungan yang sangat mahal untuk membuat, mengapa harus dibuat dan disahkan untuk konstruktor ketika itu hanya mungkin jarang digunakan? "Setter Injection" lain DI teknik yang dapat digunakan dalam situasi seperti ini.
Setter Injection tidak memaksa dependensi untuk diteruskan ke konstruktor. Sebaliknya, ketergantungan set ke-sifat umum terkena oleh objek yang di butuhkan. Seperti yang tersirat sebelumnya, motivator utama untuk melakukan hal ini meliputi:
Berikut adalah contoh bagaimana kode di atas akan terlihat seperti:
public class Person {
public Person() {}
public IDAO Address {
set { addressdao = value; }
get {
if (addressdao == null)
throw new MemberAccessException("addressdao" +
" has not been initialized");
return addressdao;
}
}
public Address GetAddress() {
// ... code that uses the addressdao object
// to fetch address details from the datasource ...
}
// Should not be called directly;
// use the public property instead
private IDAO addressdao;
Saya pikir karena semua orang telah menulis untuk DI, izinkan saya bertanya beberapa pertanyaan..
Hal ini didasarkan pada jawaban @Adam N dikirim.
Mengapa PersonService tidak lagi perlu khawatir tentang GroupMembershipService? Anda hanya disebutkan GroupMembership memiliki beberapa hal-hal(benda/sifat) itu tergantung pada. Jika GMService diperlukan dalam PService, anda'd memilikinya sebagai properti. Anda bisa pura-pura bahwa terlepas dari apakah anda disuntikkan atau tidak. Satu-satunya waktu saya'a seperti itu harus disuntikkan adalah jika GMService telah lebih spesifik anak kelas, yang anda tidak't tahu sampai saat runtime. Kemudian anda'd ingin menyuntikkan subclass. Atau jika anda ingin menggunakannya sebagai singleton atau prototipe. Jujur saja, file konfigurasi memiliki segala hardcoded sejauh apa subclass untuk jenis (interface) hal ini akan menyuntikkan selama waktu kompilasi.
EDIT
Komentar yang bagus oleh Jose Maria Arranz pada DI
DI meningkatkan kohesi dengan menghilangkan kebutuhan untuk menentukan arah dari ketergantungan dan menulis lem kode.
Palsu. Arah dependensi adalah dalam bentuk XML atau sebagai penjelasan, anda dependensi yang ditulis sebagai kode XML dan anotasi. XML dan penjelasan kode sumber.
DI mengurangi coupling dengan membuat semua komponen modular (yaitu replacable) dan well-defined interface untuk satu sama lain.
Palsu. Anda tidak perlu DI framework untuk membangun modular kode didasarkan pada antarmuka.
Sekitar diganti: dengan sangat sederhana .sifat arsip dan Kelas.forName anda dapat menentukan kelas yang dapat mengubah. Jika SETIAP kelas dari kode anda dapat diubah, Jawa ini bukan untuk anda, menggunakan bahasa scripting. By the way: penjelasan tidak dapat diubah tanpa recompiling.
Menurut saya ada satu-satunya alasan untuk DI kerangka: boiler plate pengurangan. Dengan dilakukan sistem pabrik anda dapat melakukan hal yang sama, lebih terkontrol dan lebih dapat diprediksi sebagai pilihan anda DI framework, DI kerangka janji pengurangan kode (XML dan penjelasan source code juga). Masalah ini boiler plate pengurangan hanya nyata di sangat sangat sederhana kasus (salah satu contoh per kelas dan yang sejenis), kadang-kadang di dunia nyata memetik disesuaikan objek layanan ini tidak semudah pemetaan kelas ke obyek singleton.
Dependency Injection berarti cara (yang sebenarnya setiap jalan) untuk salah satu bagian dari kode (e.g a class) untuk memiliki akses ke dependensi (bagian lain dari kode, e.g kelas-kelas lain, hal ini tergantung pada) secara modular tanpa mereka yang hardcoded (sehingga mereka dapat mengubah atau diganti secara bebas, atau bahkan dimuat di lain waktu, sesuai kebutuhan)
(dan ps , ya itu telah menjadi terlalu hyped 25$ nama untuk yang agak sederhana, konsep), saya .25
sen
Populer jawaban yang tidak bermanfaat, karena mereka menentukan ketergantungan injeksi dengan cara yang isn't berguna. Let's setuju bahwa dengan "ketergantungan" kami maksud adalah beberapa pra-ada benda lain yang kami obyek X dengan kebutuhan. Tapi kita don't mengatakan kita're lakukan "dependency injection" ketika kita mengatakan
$foo = Foo->new($bar);
Kita hanya menyebut bahwa passing parameter ke konstruktor. Kami've telah melakukan hal itu secara teratur sejak konstruktor diciptakan.
"Dependency injection" ini dianggap sebagai jenis "inversi kontrol", yang berarti bahwa beberapa logika yang diambil dari pemanggil. Yang isn't kasus ketika melewati pemanggil dalam parameter, sehingga jika yang DI, DI tidak akan berarti inversi dari kontrol.
DI berarti ada tingkat menengah antara pemanggil dan konstruktor yang mengelola dependensi. Sebuah Makefile adalah contoh sederhana dari dependency injection. "caller" adalah orang yang mengetik "membuat bar" pada baris perintah, dan "constructor" adalah compiler. Makefile menentukan bahwa bar tergantung pada foo, dan itu tidak
gcc -c foo.cpp; gcc -c bar.cpp
sebelum melakukan
gcc foo.o bar.o -o bar
Orang yang mengetik "membuat bar" tidak't perlu tahu bahwa bar tergantung pada foo. Ketergantungan itu disuntikkan antara "membuat bar" dan gcc.
Tujuan utama dari tingkat menengah bukan hanya untuk lulus dalam dependensi untuk konstruktor, tapi untuk daftar semua dependensi dalam hanya satu tempat, dan untuk menyembunyikan mereka dari coder (untuk tidak membuat coder memberikan mereka).
Biasanya tingkat menengah menyediakan pabrik untuk dibangun objek, yang harus memberikan peran yang masing-masing diminta jenis objek harus memenuhi. Yang's karena dengan memiliki tingkat menengah yang menyembunyikan rincian konstruksi, anda've yang sudah dikeluarkan abstraksi hukuman yang dikenakan oleh pabrik-pabrik, sehingga anda mungkin juga menggunakan pabrik-pabrik.