Mengapa saya memerlukan wadah IoC yang bertentangan dengan kode DI langsung? [Tutup]

598

Saya telah menggunakan Dependency Injection (DI) untuk sementara waktu, menyuntikkan baik dalam konstruktor, properti, atau metode. Saya tidak pernah merasa perlu menggunakan wadah Inversion of Control (IoC). Namun, semakin banyak saya membaca, semakin banyak tekanan yang saya rasakan dari masyarakat untuk menggunakan wadah IoC.

Saya bermain dengan .NET container seperti StructureMap , NInject , Unity , dan Funq . Saya masih gagal melihat bagaimana wadah IoC akan menguntungkan / memperbaiki kode saya.

Saya juga takut mulai menggunakan wadah di tempat kerja karena banyak rekan kerja saya akan melihat kode yang tidak mereka mengerti. Banyak dari mereka mungkin enggan mempelajari teknologi baru.

Tolong, yakinkan saya bahwa saya perlu menggunakan wadah IOC. Saya akan menggunakan argumen ini ketika saya berbicara dengan sesama pengembang di tempat kerja.

Vadim
sumber
6
Pertanyaan bagus. Saya menjawab ini dalam komentar karena saya sangat baru dengan gagasan IOC. Sepertinya ide komponen plug and play dan kopling longgar. Apakah Anda perlu menggunakan beberapa komponen lain sebagai pengganti komponen yang sekarang, apakah perlu didasarkan. Penggunaan IOC adalah untuk mempersiapkan kode untuk perubahan seperti itu, jika muncul IMO.
shahkalpesh
3
@shahkalpesh tapi saya bisa mencapai kopling longgar dengan DI sederhana. Namun, saya mengerti maksud Anda jika saya menggunakan file konfigurasi. Namun, saya tidak yakin apakah saya suka menggunakan file konfigurasi. File konfigurasi sangat verbose, kesulitan dengan refactoring, dan beralih di antara beberapa file.
Vadim
110
hanya menambahkan komentar karena sepertinya Anda mencari alasan untuk menggunakan IoC terutama; tetapi ada hal lain yang perlu dikatakan tentang masalah Anda. Anda tidak dapat menulis kode Anda ke level orang yang lebih buruk di tim Anda atau karena takut mereka tidak ingin mempelajarinya. Anda memiliki tanggung jawab tidak hanya untuk menjadi seorang profesional tetapi juga untuk masa depan Anda untuk tumbuh dan tim Anda seharusnya tidak menahan Anda.
Kevin Sheffield
4
@ Kevin, Sebenarnya kami menggunakan IOC. Tidak sesulit yang diperkirakan untuk mengajarkan semua orang tentang IOC.
Vadim
21
Saya tidak tahu mengapa moderator menutup pertanyaan-pertanyaan yang bermanfaat dan bermanfaat secara besar-besaran. Mungkin dalam kasus ini, Will tidak mengerti pertanyaannya, atau dia tidak mengerti untuk apa orang menggunakan stackexchange? Jika "tidak cocok untuk format Tanya Jawab stackexchange" maka mungkin pertimbangkan bahwa kebijakan Anda salah dan tidak semua dari ratusan pertanyaan bermanfaat ini dengan ratusan pilihan suara dan pilihan jawaban yang bermanfaat.
NickG

Jawaban:

441

Wow, tidak percaya bahwa Joel akan menyukai ini:

var svc = new ShippingService(new ProductLocator(), 
   new PricingService(), new InventoryService(), 
   new TrackingRepository(new ConfigProvider()), 
   new Logger(new EmailLogger(new ConfigProvider())));

lebih dari ini:

var svc = IoC.Resolve<IShippingService>();

Banyak orang tidak menyadari bahwa rantai dependensi Anda dapat menjadi bersarang, dan dengan cepat menjadi sulit untuk memasang mereka secara manual. Bahkan dengan pabrik, duplikasi kode Anda tidak sepadan.

Kontainer IoC bisa rumit, ya. Tetapi untuk kasus sederhana ini saya telah menunjukkan itu sangat mudah.


Oke, mari kita benarkan ini lebih jauh. Katakanlah Anda memiliki beberapa entitas atau objek model yang ingin Anda ikat ke UI pintar. UI pintar ini (kami akan menyebutnya Shindows Morms) ingin Anda menerapkan INotifyPropertyChanged sehingga dapat melakukan perubahan pelacakan & memperbarui UI yang sesuai.

"Oke, itu tidak terdengar terlalu sulit" sehingga kamu mulai menulis.

Anda mulai dengan ini:

public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime CustomerSince { get; set; }
    public string Status { get; set; }
}

..dan berakhir dengan ini :

public class UglyCustomer : INotifyPropertyChanged
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            string oldValue = _firstName;
            _firstName = value;
            if(oldValue != value)
                OnPropertyChanged("FirstName");
        }
    }

    private string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set
        {
            string oldValue = _lastName;
            _lastName = value;
            if(oldValue != value)
                OnPropertyChanged("LastName");
        }
    }

    private DateTime _customerSince;
    public DateTime CustomerSince
    {
        get { return _customerSince; }
        set
        {
            DateTime oldValue = _customerSince;
            _customerSince = value;
            if(oldValue != value)
                OnPropertyChanged("CustomerSince");
        }
    }

    private string _status;
    public string Status
    {
        get { return _status; }
        set
        {
            string oldValue = _status;
            _status = value;
            if(oldValue != value)
                OnPropertyChanged("Status");
        }
    }

    protected virtual void OnPropertyChanged(string property)
    {
        var propertyChanged = PropertyChanged;

        if(propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(property));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

Itu kode pipa yang menjijikkan, dan saya berpendapat bahwa jika Anda menulis kode seperti itu dengan tangan Anda mencuri dari klien Anda . Ada cara kerja yang lebih baik dan lebih cerdas.

Pernah dengar istilah itu, bekerja lebih pintar, bukan lebih keras?

Bayangkan beberapa pria pintar di tim Anda muncul dan berkata: "Ini cara yang lebih mudah"

Jika Anda membuat properti Anda virtual (tenang, itu bukan masalah besar) maka kita dapat menenun dalam perilaku properti itu secara otomatis. (Ini disebut AOP, tapi jangan khawatir tentang namanya, fokus pada apa yang akan dilakukan untuk Anda)

Bergantung pada alat IoC mana yang Anda gunakan, Anda bisa melakukan sesuatu yang terlihat seperti ini:

var bindingFriendlyInstance = IoC.Resolve<Customer>(new NotifyPropertyChangedWrapper());

Puf! Semua itu manual INotifyPropertyChanged BS sekarang secara otomatis dihasilkan untuk Anda, pada setiap setter properti virtual objek yang dimaksud.

Apakah ini sihir? Ya ! Jika Anda dapat mempercayai fakta bahwa kode ini melakukan tugasnya, maka Anda dapat dengan aman melewati semua properti yang membungkus mumbo-jumbo. Anda punya masalah bisnis yang harus dipecahkan.

Beberapa kegunaan lain yang menarik dari alat IoC untuk melakukan AOP:

  • Transaksi basis data deklaratif & bersarang
  • Unit kerja deklaratif & bersarang
  • Penebangan
  • Kondisi Pra / Pasca (Desain oleh Kontrak)
Ben Scheirman
sumber
28
Ups, Anda lupa membuat semua properti virtual, dan itu tidak berhasil. Apakah waktu yang Anda butuhkan untuk menghabiskan debugging ini mencuri dari klien Anda juga? (Permintaan maaf jika Anda tidak menggunakan DynamicProxy.: P)
Thom
17
Anda mengorbankan BANYAK kejelasan dengan menggunakan pembungkus INotifyPropertyChanged. Jika Anda membutuhkan lebih dari beberapa menit untuk menulis pipa ledeng untuk memulai maka Anda berada dalam bisnis yang salah. Kurang tidak lebih ketika berbicara tentang verbositas kode Anda!
overstood
39
@overstood - Saya sangat tidak setuju. Pertama, di mana kejelasan penting dalam contoh ini? Konsumen terdekat. Konsumen secara eksplisit meminta INotifyPropertyChanged. Hanya karena kita dapat membuat jenis kode ini dengan generasi kode mikro atau makro, bukan berarti itu ide yang baik untuk menulisnya. Itu INotifyPropertyChanged gunk hanya hafalan, biasa, plumbing dan benar-benar mengurangi keterbacaan kelas yang bersangkutan.
Ben Scheirman
31
Ditandai karena Anda mengacaukan pola Injeksi Ketergantungan dan pola Layanan Locator. Yang pertama dimaksudkan untuk digunakan, bukan yang terakhir. Misalnya objek (atau bahkan seluruh sistem) tidak boleh memanggil Resolve (...) untuk mendapatkan instance IShippingService. Objek yang membutuhkan Layanan IShipping harus menerima referensi ke instance yang harus mereka gunakan ketika dibangun, dan beberapa lapisan yang lebih tinggi bertanggung jawab untuk menghubungkan dua objek bersama-sama.
Nat
30
Apa pun ini (IoC, DynamicProxy, AOP atau ilmu hitam ) dapatkah seseorang menghubungkan saya dengan artikel konkret / dokumentasi tertentu dalam kerangka yang memberikan rincian lebih lanjut untuk mencapai ini?
fostandy
138

Aku bersamamu, Vadim. Kontainer IoC mengambil konsep sederhana, elegan, dan berguna, dan menjadikannya sesuatu yang harus Anda pelajari selama dua hari dengan manual 200 halaman.

Secara pribadi saya bingung bagaimana komunitas IoC mengambil artikel yang indah dan elegan oleh Martin Fowler dan mengubahnya menjadi banyak kerangka kerja yang rumit yang biasanya dengan 200-300 halaman manual.

Saya mencoba untuk tidak menghakimi (HAHA!), Tapi saya pikir orang yang menggunakan wadah IOC adalah (A) sangat pintar dan (B) kurang empati untuk orang yang tidak sepintar mereka. Semuanya masuk akal bagi mereka, sehingga mereka kesulitan memahami bahwa banyak programmer biasa akan menemukan konsep yang membingungkan. Itu adalah kutukan pengetahuan . Orang-orang yang memahami wadah IoC kesulitan mempercayai bahwa ada orang yang tidak memahaminya.

Manfaat paling berharga dari menggunakan wadah IoC adalah Anda dapat memiliki sakelar konfigurasi di satu tempat yang memungkinkan Anda mengubah antara, misalnya, mode uji dan mode produksi. Sebagai contoh, misalkan Anda memiliki dua versi kelas akses basis data Anda ... satu versi yang masuk secara agresif dan melakukan banyak validasi, yang Anda gunakan selama pengembangan, dan versi lain tanpa pencatatan atau validasi yang sangat cepat untuk produksi. Sangat menyenangkan untuk dapat beralih di antara mereka di satu tempat. Di sisi lain, ini adalah masalah yang cukup sepele yang mudah ditangani dengan cara yang lebih sederhana tanpa kerumitan wadah IOC.

Saya percaya bahwa jika Anda menggunakan wadah IoC, kode Anda menjadi, terus terang, jauh lebih sulit dibaca. Jumlah tempat yang harus Anda perhatikan untuk mengetahui apa yang coba dilakukan oleh kode naik setidaknya satu. Dan di suatu tempat di surga seorang malaikat berteriak.

Joel Spolsky
sumber
81
Saya pikir Anda memiliki fakta-fakta Anda bercampur Joel. IOC dan kontainer datang lebih dulu, kemudian muncul risalah oleh Martin, bukan sebaliknya.
Glenn Block
55
Eesh sebagian besar wadah membiarkan Anda pergi dengan sangat cepat. Dengan autofac, struktur peta, unity, ninject, dll Anda harus dapat membuat wadah bekerja sekitar 5 menit. Ya, mereka memiliki fitur-fitur canggih, tetapi Anda tidak perlu mereka turun dari tanah.
Glenn Block
58
Joel saya dulu berpikir dengan cara yang sama seperti Anda. Lalu saya benar-benar dan serius menghabiskan waktu lima menit untuk mencari tahu cara menjalankan pengaturan paling dasar dan kagum pada aturan 80/20 yang sedang beraksi. Itu membuat kode jauh lebih bersih terutama dalam kasus-kasus sederhana.
Justin Bozonier
55
Saya sudah pemrograman kurang dari dua tahun dan saya membaca wiki Ninject dan mendapatkannya. Saya telah menggunakan DI di banyak tempat sejak saat itu. Apakah kamu melihat itu? Kurang dari dua tahun dan membaca beberapa halaman di wiki. Saya bukan penyihir atau apa pun. Saya tidak menganggap diri saya jenius, tetapi mudah bagi saya untuk mengerti. Saya tidak bisa membayangkan seseorang yang benar-benar mengerti OO tidak dapat menangkapnya. Juga, manual 200-300 halaman apa yang Anda maksud? Saya belum pernah melihat itu. Semua wadah IoC yang saya gunakan cukup singkat dan to the point.
JR Garcia
35
+1 ditulis dengan baik dan dipikirkan. Satu hal yang saya pikir banyak orang tidak sadari adalah bahwa menambahkan kerangka kerja atau semacamnya untuk "secara ajaib" menangani sesuatu secara otomatis menambah kompleksitas dan keterbatasan pada suatu sistem. Terkadang itu sepadan, tetapi seringkali sesuatu diabaikan dan entropi dilepaskan ke dalam kode yang mencoba untuk memperbaiki masalah yang ditegakkan oleh keterbatasan. Ini mungkin menghemat waktu di muka, tetapi orang harus menyadari bahwa mungkin perlu biaya 100 kali lipat untuk mencoba memperbaiki beberapa masalah yang tidak jelas, hanya segelintir orang yang benar-benar memiliki pengetahuan yang cukup untuk menyelesaikannya.
kemiller2002
37

Agaknya tidak ada yang memaksa Anda untuk menggunakan kerangka wadah DI. Anda sudah menggunakan DI untuk memisahkan kelas dan meningkatkan kemampuan tes, sehingga Anda mendapatkan banyak manfaat. Singkatnya, Anda mendukung kesederhanaan, yang umumnya merupakan hal yang baik.

Jika sistem Anda mencapai tingkat kerumitan di mana DI manual menjadi tugas (yaitu, meningkatkan pemeliharaan), timbang itu terhadap kurva pembelajaran tim dari kerangka wadah DI.

Jika Anda memerlukan kontrol lebih besar atas manajemen seumur hidup ketergantungan (yaitu, jika Anda merasa perlu menerapkan pola Singleton), lihat wadah DI.

Jika Anda menggunakan wadah DI, gunakan hanya fitur yang Anda butuhkan. Lewati file konfigurasi XML dan konfigurasikan dalam kode jika cukup. Tetap menggunakan injeksi konstruktor. Dasar-dasar Unity atau StructureMap dapat diringkas menjadi beberapa halaman.

Ada posting blog yang bagus oleh Mark Seemann tentang ini: Kapan menggunakan Kontainer DI

TrueWill
sumber
Responnya sangat bagus. Satu-satunya hal yang berbeda pendapat saya adalah apakah injeksi manual PERNAH dapat dianggap kurang kompleks atau tidak.
wekempf
+1. Salah satu jawaban terbaik di sini. Terima kasih juga untuk tautan artikel blog.
stakx - tidak lagi berkontribusi
1
+1 untuk sudut pandang seimbang. Kontainer IoC tidak selalu baik, dan tidak selalu buruk. Jika Anda tidak melihat suatu kebutuhan, jangan menggunakannya. Hanya tahu bahwa alat itu ada di sana jika Anda melihat suatu kebutuhan.
Phil
32

Menurut pendapat saya manfaat nomor satu dari IoC adalah kemampuan untuk memusatkan konfigurasi dependensi Anda.

Jika saat ini Anda menggunakan injeksi Ketergantungan kode Anda mungkin terlihat seperti ini

public class CustomerPresenter
{
  public CustomerPresenter() : this(new CustomerView(), new CustomerService())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

Jika Anda menggunakan kelas IoC statis, sebagai lawan dari, IMHO file konfigurasi yang lebih membingungkan, Anda dapat memiliki sesuatu seperti ini:

public class CustomerPresenter
{
  public CustomerPresenter() : this(IoC.Resolve<ICustomerView>(), IoC.Resolve<ICustomerService>())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

Kemudian, kelas Static IoC Anda akan terlihat seperti ini, saya menggunakan Unity di sini.

public static IoC
{
   private static readonly IUnityContainer _container;
   static IoC()
   {
     InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerView, CustomerView>();
      _container.RegisterType<ICustomerService, CustomerService>();
      // all other RegisterTypes and RegisterInstances can go here in one file.
      // one place to change dependencies is good.
   }
}
bendewey
sumber
11
Ben, apa yang Anda bicarakan adalah benar-benar Lokasi Layanan, bukan Injeksi Ketergantungan - ada perbedaan. Saya selalu lebih suka yang pertama daripada yang terakhir. stevenharman.net/blog/archive/2009/09/25/…
stevenharman
23
Alih-alih melakukan IoC.Resolve <T> di konstruktor default Anda, Anda harus meningkatkan kemampuan kontributor IOC untuk membangun objek untuk Anda. Singkirkan konstruktor kosong itu, dan biarkan wadah IOC membangun objek untuk Anda. var presenter = IoC.Resolve <CustomerPresenter> (); voila! semua dependensi sudah terhubung.
Ben Scheirman
12
Kelemahan dari sentralisasi konfigurasi semacam ini adalah de-kontekstualisasi pengetahuan. Joel menunjukkan masalah ini dengan mengatakan, "kode menjadi, terus terang, jauh lebih sulit dibaca".
Scott Bellware
6
@sbellware, pengetahuan apa? Antarmuka yang diambil sebagai dependensi primer berfungsi sebagai kontrak dan harus cukup untuk menyampaikan tujuan & perilaku itu, bukan? Saya kira Anda mungkin merujuk pada pengetahuan yang diperoleh dari spelunking ke dalam implementasi kontrak, dalam hal mana Anda perlu melacak konfigurasi yang sesuai? Saya mengandalkan komputer (R # dalam VS, IntelliJ, dll.) Untuk itu karena mereka hebat dalam menavigasi node & tepi grafik; Saya memesan tumpukan otak saya untuk konteks tempat kerja yang saat ini saya fokuskan.
stevenharman
7
Steve, saya tahu teks .NET dan berjalan itu selama bertahun-tahun. Saya juga memiliki keakraban yang akrab dengan mode dan bentuk lain, dan saya mengerti secara mendalam bahwa ada pertukaran nyata. Saya tahu bagaimana dan kapan harus melakukan pengorbanan itu, tetapi keragaman dalam kehidupan kerja saya setidaknya memungkinkan saya memahami perspektif Joel secara nyata. Daripada menguliahi saya tentang alat dan trik yang telah saya gunakan lebih lama dari kebanyakan .NET mono-culturists, katakan sesuatu yang tidak saya ketahui. Saya memesan tumpukan otak saya untuk beragam, pemikiran kritis daripada stasis alt.net dan ortodoksi.
Scott Bellware
32

Kontainer IoC juga baik untuk memuat dependensi kelas yang sangat bersarang. Sebagai contoh jika Anda memiliki kode berikut menggunakan Depedency Injection.

public void GetPresenter()
{
    var presenter = new CustomerPresenter(new CustomerService(new CustomerRepository(new DB())));
}

class CustomerPresenter
{
    private readonly ICustomerService service;
    public CustomerPresenter(ICustomerService service)
    {
        this.service = service;
    }
}

class CustomerService
{
    private readonly IRespository<Customer> repository;
    public CustomerService(IRespository<Customer> repository)
    {
        this.repository = repository;
    }
}

class CustomerRepository : IRespository<Customer>
{
    private readonly DB db;
    public CustomerRepository(DB db)
    {
        this.db = db;
    }
}

class DB { }

Jika Anda memiliki semua dependensi ini dimuat ke dalam dan wadah IoC Anda bisa Menyelesaikan Layanan Pelanggan dan semua dependensi anak akan secara otomatis diselesaikan.

Sebagai contoh:

public static IoC
{
   private IUnityContainer _container;
   static IoC()
   {
       InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerService, CustomerService>();
      _container.RegisterType<IRepository<Customer>, CustomerRepository>();
   }

   static T Resolve<T>()
   {
      return _container.Resolve<T>();
   }
}

public void GetPresenter()
{
   var presenter = IoC.Resolve<CustomerPresenter>();
   // presenter is loaded and all of its nested child dependencies 
   // are automatically injected
   // -
   // Also, note that only the Interfaces need to be registered
   // the concrete types like DB and CustomerPresenter will automatically 
   // resolve.
}
bendewey
sumber
Tetapi kelas IoC disebut menggunakan service locator anti-pattern. Seharusnya_container dilewatkan menggunakan konstruktor DI alih-alih memanggil statis T Selesaikan <T> ()?
Michael Freidgeim
@bendewey Contoh Anda menyiratkan bahwa ia secara otomatis meneruskan argumen ke konstruktor untuk Layanan Pelanggan baru dan Gudang Pelanggan baru. Apakah ini cara kerjanya? Bagaimana jika Anda memiliki parameter tambahan lainnya juga?
Brain2000
28

Saya penggemar pemrograman deklaratif (lihat berapa banyak pertanyaan SQL yang saya jawab), tetapi wadah IoC yang saya lihat tampaknya terlalu misterius untuk kebaikan mereka sendiri.

... atau mungkin pengembang wadah IoC tidak mampu menulis dokumentasi yang jelas.

... atau keduanya benar untuk satu derajat atau lainnya.

Saya tidak berpikir konsep wadah IoC buruk. Tetapi implementasinya harus kuat (yaitu, fleksibel) cukup untuk berguna dalam beragam aplikasi, namun sederhana dan mudah dipahami.

Mungkin enam dari satu dan setengah lusin yang lain. Sebuah aplikasi nyata (bukan mainan atau demo) pasti kompleks, terhitung untuk banyak kasus sudut dan pengecualian terhadap aturan. Entah Anda merangkum kompleksitas dalam kode imperatif, atau yang lain dalam kode deklaratif. Tetapi Anda harus mewakilinya di suatu tempat.

Bill Karwin
sumber
5
Saya suka pengamatan Anda tentang keberadaan kompleksitas yang tidak dapat dihindari. Perbedaan antara kabel dependensi berbasis manual dan manual adalah bahwa dengan menggunakan kontainer, Anda dapat fokus pada setiap komponen secara individual, dan dengan demikian mengelola peningkatan kompleksitas dengan cara yang cukup linier. Sistem dengan kabel ketergantungan manual lebih sulit untuk berkembang, karena tindakan mengonfigurasi satu komponen sulit untuk diisolasi dari mengonfigurasi segala sesuatu yang lain.
Nicholas Blumhardt
Saya suka AspectJ. Ini bukan Java, tapi juga bukan XML. Ini semacam IS Java, rasanya seperti Java dengan ekstensi. Saya lebih suka menjaga aspek saya keluar dari POJO saya dan dalam logika bisnis saya. Saya hampir ingin tidak menggunakan IoC saya. Ada terlalu banyak XML dalam perkabelan alat, IMHO. Jika saya harus memiliki file konfigurasi, biarkan mereka menjadi skrip BeanShell. / Java, terapkan .Net workalikes Anda di sini ->.
Chris K
2
Saya tentu mengerti jawaban Anda, tetapi saya pikir sebagian besar masalahnya adalah bahwa banyak kerangka kerja IoC (dan, sungguh, banyak kerangka kerja lain untuk hal-hal lain juga) dijelaskan dan didokumentasikan untuk orang lain yang sudah sangat akrab dengan konsep dan terminologi yang diperlukan. Saya belajar banyak tentang ini karena baru saja mewarisi kode programmer lain. Saya berjuang untuk memahami mengapa kode tersebut menggunakan IoC ketika 'objek grafik' cukup kecil dan tidak ada cakupan tes nyata dari kode. Sebagai seorang programmer SQL, Anda mungkin bisa lebih menghargai menggunakan IoC dengan ORM.
Kenny Evitt
1
@ KennyEvitt, poin bagus, sepertinya terlalu dini untuk menggunakan kerangka kerja IoC jika seseorang memiliki aplikasi sederhana dan bahkan belum repot-repot memiliki cakupan pengujian yang baik.
Bill Karwin
27

Menggunakan wadah sebagian besar tentang perubahan dari gaya inisialisasi / skrip inisialisasi dan konfigurasi ke yang deklaratif . Ini mungkin memiliki beberapa efek menguntungkan yang berbeda:

  • Mengurangi rutinitas startup program utama hairball .
  • Mengaktifkan kemampuan konfigurasi ulang waktu penempatan yang cukup dalam.
  • Menjadikan gaya ketergantungan-suntik menjadi jalan dengan resistensi paling rendah untuk pekerjaan baru.

Tentu saja, mungkin ada kesulitan:

  • Kode yang membutuhkan manajemen startup / shutdown / siklus hidup yang kompleks mungkin tidak mudah diadaptasi ke sebuah wadah.
  • Anda mungkin harus menavigasi masalah pribadi, proses, dan budaya tim - tetapi kemudian, itulah mengapa Anda bertanya ...
  • Beberapa toolkit dengan cepat menjadi kelas berat sendiri, mendorong semacam ketergantungan yang dalam yang dimulai oleh banyak wadah DI sebagai reaksi balik.
Jeffrey Hantin
sumber
23

Bagi saya, sepertinya Anda sudah membuat wadah IoC Anda sendiri (menggunakan berbagai pola yang dijelaskan oleh Martin Fowler) dan bertanya mengapa implementasi orang lain lebih baik daripada Anda.

Jadi, Anda memiliki banyak kode yang sudah berfungsi. Dan bertanya-tanya mengapa Anda ingin menggantinya dengan implementasi orang lain.

Pro untuk mempertimbangkan wadah IoC pihak ketiga

  • Anda memperbaiki bug secara gratis
  • Desain perpustakaan mungkin lebih baik dari milik Anda
  • Orang mungkin sudah terbiasa dengan perpustakaan tertentu
  • Perpustakaan mungkin lebih cepat dari milik Anda
  • Ini mungkin memiliki beberapa fitur yang ingin Anda terapkan tetapi tidak pernah punya waktu untuk (apakah Anda memiliki locater layanan?)

Cons

  • Anda mendapatkan bug yang diperkenalkan, gratis :)
  • Desain perpustakaan mungkin lebih buruk daripada milik Anda
  • Anda harus mempelajari API baru
  • Terlalu banyak fitur yang tidak akan Anda gunakan
  • Biasanya lebih sulit untuk men-debug kode yang tidak Anda tulis
  • Bermigrasi dari wadah IoC sebelumnya mungkin membosankan

Jadi, timbang pro Anda melawan kontra Anda dan buat keputusan.

Sam Saffron
sumber
Saya setuju dengan Anda Sam - DI adalah jenis IoC, jadi jika poster asli melakukan DI, mereka sudah melakukan IoC sehingga pertanyaan sebenarnya adalah mengapa roll sendiri.
Jamie Love
3
Saya tidak setuju. IOC adalah cara melakukan DI. Kontainer IOC melakukan DI dengan mengendalikan instantiasi tipe menggunakan refleksi runtime dinamis untuk memuaskan dan menyuntikkan dependensi. OP menyarankan dia melakukan DI statis dan merenungkan mengapa dia perlu menukar manfaat dari kompilasi waktu memeriksa untuk manfaat yang sering dikaitkan dengan refleksi runtime dinamis IOC.
Maxm007
17

Saya pikir sebagian besar nilai IOC dikumpulkan dengan menggunakan DI. Karena Anda sudah melakukan itu, sisa manfaatnya bersifat inkremental.

Nilai yang Anda peroleh akan tergantung pada jenis aplikasi yang sedang Anda kerjakan:

  • Untuk multi-tenant, wadah IoC dapat menangani beberapa kode infrastruktur untuk memuat sumber daya klien yang berbeda. Saat Anda membutuhkan komponen yang spesifik untuk klien, gunakan pemilih khusus untuk menangani logika dan jangan khawatir tentang hal itu dari kode klien Anda. Anda tentu dapat membangun ini sendiri tetapi inilah contoh bagaimana IoC dapat membantu.

  • Dengan banyak titik ekstensibilitas, IoC dapat digunakan untuk memuat komponen dari konfigurasi. Ini adalah hal yang umum untuk dibangun tetapi alat disediakan oleh wadah.

  • Jika Anda ingin menggunakan AOP untuk beberapa masalah lintas sektoral, IoC menyediakan kait untuk mencegat pemanggilan metode. Ini jarang dilakukan secara ad-hoc pada proyek tetapi IoC membuatnya lebih mudah.

Saya telah menulis fungsionalitas seperti ini sebelumnya, tetapi jika saya memerlukan semua fitur ini sekarang saya lebih suka menggunakan alat yang sudah dibangun dan diuji jika cocok dengan arsitektur saya.

Seperti yang disebutkan oleh orang lain, Anda juga dapat mengkonfigurasi secara terpusat kelas mana yang ingin Anda gunakan. Meskipun ini bisa menjadi hal yang baik, itu datang dengan biaya penyesatan dan komplikasi. Komponen inti untuk sebagian besar aplikasi tidak banyak diganti sehingga trade-off agak sulit dibuat.

Saya menggunakan wadah IoC dan menghargai fungsi tetapi harus mengakui bahwa saya telah memperhatikan trade-off: Kode saya menjadi lebih jelas di tingkat kelas dan kurang jelas di tingkat aplikasi (yaitu memvisualisasikan aliran kontrol).

Steven Lyons
sumber
16

Saya seorang pecandu IOC yang sedang pulih. Saya merasa sulit untuk membenarkan menggunakan IOC untuk DI dalam banyak kasus belakangan ini. Kontainer IOC mengorbankan pemeriksaan waktu kompilasi dan konon sebagai imbalannya memberi Anda pengaturan yang "mudah", manajemen seumur hidup yang kompleks, dan penemuan dependensi saat berjalan. Saya menemukan hilangnya waktu kompilasi memeriksa dan menghasilkan run time magic / pengecualian, tidak sebanding dengan lonceng dan peluit di sebagian besar kasus. Dalam aplikasi perusahaan besar mereka dapat membuatnya sangat sulit untuk mengikuti apa yang sedang terjadi.

Saya tidak membeli argumen sentralisasi karena Anda dapat memusatkan pengaturan statis dengan sangat mudah juga dengan menggunakan pabrik abstrak untuk aplikasi Anda dan secara religius menunda pembuatan objek ke pabrik abstrak yaitu melakukan DI yang tepat.

Mengapa tidak melakukan DI statis bebas sulap seperti ini:

interface IServiceA { }
interface IServiceB { }
class ServiceA : IServiceA { }
class ServiceB : IServiceB { }

class StubServiceA : IServiceA { }
class StubServiceB : IServiceB { }

interface IRoot { IMiddle Middle { get; set; } }
interface IMiddle { ILeaf Leaf { get; set; } }
interface ILeaf { }

class Root : IRoot
{
    public IMiddle Middle { get; set; }

    public Root(IMiddle middle)
    {
        Middle = middle;
    }

}

class Middle : IMiddle
{
    public ILeaf Leaf { get; set; }

    public Middle(ILeaf leaf)
    {
        Leaf = leaf;
    }
}

class Leaf : ILeaf
{
    IServiceA ServiceA { get; set; }
    IServiceB ServiceB { get; set; }

    public Leaf(IServiceA serviceA, IServiceB serviceB)
    {
        ServiceA = serviceA;
        ServiceB = serviceB;
    }
}


interface IApplicationFactory
{
    IRoot CreateRoot();
}

abstract class ApplicationAbstractFactory : IApplicationFactory
{
    protected abstract IServiceA ServiceA { get; }
    protected abstract IServiceB ServiceB { get; }

    protected IMiddle CreateMiddle()
    {
        return new Middle(CreateLeaf());
    }

    protected ILeaf CreateLeaf()
    {
        return new Leaf(ServiceA,ServiceB);
    }


    public IRoot CreateRoot()
    {
        return new Root(CreateMiddle());
    }
}

class ProductionApplication : ApplicationAbstractFactory
{
    protected override IServiceA ServiceA
    {
        get { return new ServiceA(); }
    }

    protected override IServiceB ServiceB
    {
        get { return new ServiceB(); }
    }
}

class FunctionalTestsApplication : ApplicationAbstractFactory
{
    protected override IServiceA ServiceA
    {
        get { return new StubServiceA(); }
    }

    protected override IServiceB ServiceB
    {
        get { return new StubServiceB(); }
    }
}


namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ProductionApplication();
            var root = factory.CreateRoot();

        }
    }

    //[TestFixture]
    class FunctionalTests
    {
        //[Test]
        public void Test()
        {
            var factory = new FunctionalTestsApplication();
            var root = factory.CreateRoot();
        }
    }
}

Konfigurasi kontainer Anda adalah implementasi pabrik abstrak Anda, pendaftaran Anda adalah implementasi dari anggota abstrak. Jika Anda memerlukan ketergantungan tunggal baru, cukup tambahkan properti abstrak lain ke pabrik abstrak. Jika Anda membutuhkan ketergantungan sementara, cukup tambahkan metode lain dan kirimkan sebagai Fungsi <>.

Keuntungan:

  • Semua pengaturan dan konfigurasi pembuatan objek terpusat.
  • Konfigurasi hanyalah kode
  • Kompilasi pemeriksaan waktu memudahkan pemeliharaan karena Anda tidak bisa lupa memperbarui pendaftaran.
  • Tidak ada sihir refleksi run-time

Saya merekomendasikan skeptis untuk mencobanya proyek lapangan hijau berikutnya dan jujur ​​bertanya pada diri sendiri pada titik mana Anda membutuhkan wadah. Sangat mudah untuk memasukkan kontainer IOC nantinya karena Anda hanya mengganti implementasi pabrik dengan modul konfigurasi Container IOC.

Maxm007
sumber
Bagi saya ini sepertinya bukan argumen melawan IoC, tetapi argumen terhadap kerangka kerja IoC yang dapat dikonfigurasi . Bukankah pabrik-pabrik Anda di sini baru saja berubah menjadi IOC sederhana yang dikodekan dengan keras?
Mr.Mindor
Saya pikir ketika poster menyebutkan IoC, kerangka wadah IoC dimaksudkan. Terima kasih atas luncuran asli ini membantu meyakinkan saya untuk pergi tanpa kerangka kerja. Jadi jika itu dapat dikonfigurasi dalam tes dan kode produksi apa keuntungan dari kerangka kerja "dapat dikonfigurasi"?
huggie
Dari pemahaman saya tentang terminologi, Inversion Of Control berarti menemukan dependensi saat run-time. Jadi ya, bisa dibilang pabrik itu pada dasarnya adalah wadah yang keras atau statis, tapi itu bukan wadah IOC. Jenis diselesaikan pada waktu kompilasi. Ini adalah argumen yang menentang penggunaan kerangka kerja yang dapat dikonfigurasi yang menggunakan pencarian kamus untuk menyelesaikan tipe saat runtime.
Maxm007
14

Manfaat terbesar menggunakan wadah IoC untuk saya (secara pribadi, saya menggunakan Ninject) adalah untuk menghilangkan pengaturan dan berbagai objek negara global lainnya.

Saya tidak memprogram untuk web, milik saya adalah aplikasi konsol dan di banyak tempat jauh di pohon objek saya harus memiliki akses ke pengaturan atau metadata yang ditentukan oleh pengguna yang dibuat pada cabang pohon objek yang sepenuhnya terpisah. Dengan IoC saya hanya memberitahu Ninject untuk memperlakukan Pengaturan sebagai singleton (karena selalu ada hanya satu instance dari mereka), meminta Pengaturan atau Kamus di konstruktor dan presto ... mereka secara ajaib muncul ketika saya membutuhkannya!

Tanpa menggunakan wadah IoC saya harus melewati pengaturan dan / atau metadata turun melalui 2, 3, ..., dan objek sebelum benar-benar digunakan oleh objek yang membutuhkannya.

Ada banyak manfaat lain untuk wadah DI / IoC karena orang lain telah merinci di sini dan beralih dari gagasan membuat benda ke meminta benda bisa sangat membekas, tetapi menggunakan DI sangat membantu bagi saya dan tim saya sehingga mungkin Anda dapat menambahkannya ke gudang Anda!

Jeffrey Cameron
sumber
2
Ini merupakan nilai tambah yang besar. Tidak percaya tidak ada orang lain yang menyebutkannya sebelumnya. Tanpa perlu melewati atau menyimpan objek sementara membuat kode lebih bersih.
Finglas
1
@qble objek global berfungsi dengan baik ... jika Anda ingin basis kode Anda menjadi mimpi buruk yang tidak dapat diuji yang semakin sulit untuk dimodifikasi seiring waktu. semoga beruntung dengan itu.
Jeffrey Cameron
2
@JeffreyCameron IoC Container adalah objek global. Itu tidak menghapus ketergantungan global Anda.
qble
3
Benar, namun wadah IoC transparan untuk seluruh aplikasi. Anda menyebutnya sekali saat startup untuk mendapatkan sebuah instance dan kemudian itu yang terakhir Anda lihat. Dengan objek global, kode Anda dipenuhi dengan dependensi keras
Jeffrey Cameron
1
@qble menggunakan pabrik untuk membuat contoh sementara dengan cepat. Pabrik adalah layanan tunggal. Jika instance transient memerlukan sesuatu dari wadah IOC kemudian kirimkan ke pabrik dan minta pabrik untuk meneruskan ketergantungannya ke instance transient.
Jeffrey Cameron
14

Kerangka kerja IoC sangat baik jika Anda ingin ...

  • ... Membuang keamanan tipe. Banyak (semua?) Kerangka kerja IoC memaksa Anda untuk mengeksekusi kode jika Anda ingin memastikan semuanya terhubung dengan benar. "Hei! Kuharap semuanya sudah diatur sehingga inisialisasi 100 kelas ini tidak akan gagal dalam produksi, melempar pengecualian null-pointer!"

  • ... sampah kode Anda dengan GLOBALS (kerangka IOC adalah semua tentang mengubah negara global).

  • ... tulis kode jelek dengan dependensi tidak jelas yang sulit untuk diperbaiki karena Anda tidak akan pernah tahu apa yang bergantung pada apa.

Masalahnya dengan IoC adalah orang-orang yang menggunakannya terbiasa menulis kode seperti ini

public class Foo {
    public Bar Apa {get;set;}
    Foo() {
        Apa = new Bar();
    }
}

yang jelas cacat karena ketergantungan antara Foo dan Bar terprogram. Kemudian mereka menyadari akan lebih baik menulis kode seperti

public class Foo {
    public IBar Apa {get;set;}
    Foo() {
        Apa = IoC<IBar>();
    }
}

yang juga cacat, tetapi kurang begitu jelas. Di Haskell tipe Foo()akan IO Footetapi Anda benar-benar tidak ingin- IObagian dan harus menjadi tanda peringatan bahwa ada sesuatu yang salah dengan desain Anda jika Anda mendapatkannya.

Untuk menghilangkannya (IO-part), dapatkan semua keuntungan dari IoC-frameworks dan tidak ada kekurangannya Anda bisa menggunakan pabrik abstrak.

Solusi yang tepat akan menjadi sesuatu seperti

data Foo = Foo { apa :: Bar }

atau mungkin

data Foo = forall b. (IBar b) => Foo { apa :: b }

dan menyuntikkan (tapi saya tidak akan menyebutnya menyuntikkan) Bar.

Juga: lihat video ini dengan Erik Meijer (penemu LINQ) di mana ia mengatakan bahwa DI adalah untuk orang yang tidak tahu matematika (dan saya sangat setuju): http://www.youtube.com/watch?v = 8Mttjyf-8P4

Tidak seperti Mr. Spolsky, saya tidak percaya bahwa orang yang menggunakan IoC-frameworks sangat cerdas - saya hanya percaya mereka tidak tahu matematika.

finnsson
sumber
9
buang tipe keselamatan - kecuali contoh Anda masih tipe aman, itulah antarmuka. Anda tidak membagikan tipe objek, tetapi tipe antarmuka. Dan tipe safety tidak menghilangkan pengecualian referensi nol, Anda bisa dengan senang hati melewatkan referensi null sebagai parameter safe type - itu sama sekali bukan masalah keamanan tipe.
blowdart
Anda membuang keamanan jenis karena Anda tidak tahu apakah jenisnya ditransfer atau tidak dalam wadah-IOC (jenis IoC <IBar> tidak IBar, itu sebenarnya IO IBar) .. Dan ya, dalam contoh-Haskell Saya tidak bisa mendapatkan referensi-nol.
finnsson
Jenis keamanan tidak benar-benar tentang objek yang sedang dihubungkan, setidaknya tidak di Java / C #, itu hanya tentang objek yang bertipe tepat. Dan ConcreteClass myClass = null masih dari jenis yang tepat. Jika Anda ingin memberlakukan bukan nol maka kontrak kode ikut bermain.
blowdart
2
Saya setuju dengan poin 1 Anda sampai batas tertentu, saya ingin penjelasan tentang ke-2 dan ke-3 tidak pernah menjadi masalah bagi saya. Sampel C # Anda menggunakan wadah IoC sebagai pencari lokasi, kesalahan umum. Dan membandingkan C # vs Haskell = apel vs jeruk.
Mauricio Scheffer
2
Anda tidak membuang keamanan jenis. Beberapa (jika tidak sebagian besar) wadah DI memungkinkan Anda untuk memverifikasi grafik objek lengkap setelah proses pendaftaran. Ketika melakukan ini, Anda dapat mencegah aplikasi memulai kesalahan konfigurasi (gagal cepat) dan aplikasi saya selalu memiliki beberapa unit pengujian yang memanggil konfigurasi produksi untuk memungkinkan saya menemukan kesalahan ini selama pengujian. Saya akan menyarankan semua orang menggunakan wadah IoC untuk menulis beberapa unit test yang memastikan semua objek tingkat atas dapat diselesaikan.
Steven
10

Saya telah menemukan bahwa mengimplementasikan Dependency Injection dengan benar cenderung memaksa programmer untuk menggunakan berbagai praktik pemrograman lain yang membantu meningkatkan testabilitas, fleksibilitas, kemudahan pemeliharaan, dan skalabilitas kode: praktik seperti Prinsip Tanggung Jawab Tunggal, Pemisahan Masalah, dan pengkodean terhadap API. Rasanya seperti saya dipaksa untuk menulis lebih banyak modular, kelas dan metode berukuran gigitan, yang membuat kode lebih mudah dibaca, karena dapat diambil dalam potongan berukuran gigitan.

Tetapi juga cenderung membuat pohon ketergantungan yang agak besar, yang jauh lebih mudah dikelola melalui kerangka kerja (terutama jika Anda menggunakan konvensi) daripada dengan tangan. Hari ini saya ingin menguji sesuatu dengan sangat cepat di LINQPad, dan saya pikir akan terlalu merepotkan untuk membuat kernel dan memuat modul-modul saya, dan akhirnya saya menulis ini dengan tangan:

var merger = new SimpleWorkflowInstanceMerger(
    new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
    new WorkflowAnswerRowUtil(
        new WorkflowFieldAnswerEntMapper(),
        new ActivityFormFieldDisplayInfoEntMapper(),
        new FieldEntMapper()),
    new AnswerRowMergeInfoRepository());

Dalam retrospeksi, akan lebih cepat untuk menggunakan kerangka kerja IoC, karena modul mendefinisikan hampir semua hal ini dengan konvensi.

Setelah menghabiskan beberapa waktu mempelajari jawaban dan komentar pada pertanyaan ini, saya yakin bahwa orang-orang yang menentang menggunakan wadah IoC tidak mempraktikkan injeksi ketergantungan yang sebenarnya. Contoh-contoh yang saya lihat adalah praktik yang biasanya bingung dengan injeksi ketergantungan. Beberapa orang mengeluh tentang kesulitan "membaca" kode. Jika dilakukan dengan benar, sebagian besar kode Anda harus identik ketika menggunakan DI dengan tangan seperti ketika menggunakan wadah IoC. Perbedaannya harus berada sepenuhnya dalam beberapa "titik peluncuran" dalam aplikasi.

Dengan kata lain, jika Anda tidak menyukai wadah IOC, Anda mungkin tidak melakukan Injeksi Ketergantungan seperti yang seharusnya dilakukan.

Poin lain: Ketergantungan Injeksi benar-benar tidak dapat dilakukan dengan tangan jika Anda menggunakan refleksi di mana saja. Meskipun saya benci refleksi yang dilakukan terhadap navigasi kode, Anda harus mengenali bahwa ada area tertentu yang tidak dapat dihindari. ASP.NET MVC, misalnya, mencoba untuk instantiate controller melalui refleksi pada setiap permintaan. Untuk melakukan injeksi dependensi dengan tangan, Anda harus menjadikan setiap controller "root konteks", seperti:

public class MyController : Controller
{
    private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
    public MyController()
    {
        _simpleMerger = new SimpleWorkflowInstanceMerger(
            new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
            new WorkflowAnswerRowUtil(
                new WorkflowFieldAnswerEntMapper(),
                new ActivityFormFieldDisplayInfoEntMapper(),
                new FieldEntMapper()),
            new AnswerRowMergeInfoRepository())
    }
    ...
}

Sekarang bandingkan ini dengan memungkinkan kerangka kerja DI melakukannya untuk Anda:

public MyController : Controller
{
    private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
    public MyController(ISimpleWorkflowInstanceMerger simpleMerger)
    {
        _simpleMerger = simpleMerger;
    }
    ...
}

Menggunakan kerangka kerja DI, perhatikan bahwa:

  • Saya dapat menguji unit kelas ini. Dengan membuat tiruan ISimpleWorkflowInstanceMerger, saya bisa menguji apakah itu digunakan dengan cara yang saya perkirakan, tanpa perlu koneksi database atau apa pun.
  • Saya menggunakan kode yang jauh lebih sedikit, dan kode ini jauh lebih mudah dibaca.
  • Jika salah satu ketergantungan saya berubah, saya tidak perlu melakukan perubahan apa pun pada controller. Ini sangat baik ketika Anda mempertimbangkan bahwa banyak pengontrol cenderung menggunakan beberapa dependensi yang sama.
  • Saya tidak pernah secara eksplisit merujuk kelas dari lapisan data saya. Aplikasi web saya hanya bisa menyertakan referensi ke proyek yang berisi ISimpleWorkflowInstanceMergerantarmuka. Ini memungkinkan saya untuk memecah aplikasi menjadi modul terpisah, dan mempertahankan arsitektur multi-tier yang sebenarnya, yang pada gilirannya membuat semuanya menjadi lebih fleksibel.

Aplikasi web tipikal akan memiliki beberapa pengontrol. Semua rasa sakit melakukan DI dengan tangan di setiap pengontrol akan bertambah seiring dengan bertambahnya aplikasi Anda. Jika Anda memiliki aplikasi dengan hanya satu konteks root, yang tidak pernah mencoba untuk instantiate layanan dengan refleksi, maka ini bukan masalah besar. Namun demikian, aplikasi apa pun yang menggunakan Dependency Injection akan menjadi sangat mahal untuk dikelola setelah mencapai ukuran tertentu, kecuali jika Anda menggunakan semacam kerangka kerja untuk mengelola grafik dependensi.

StriplingWarrior
sumber
9

Setiap kali Anda menggunakan kata kunci "baru", Anda menciptakan ketergantungan kelas yang konkret dan bel alarm kecil akan berbunyi di kepala Anda. Menjadi lebih sulit untuk menguji objek ini secara terpisah. Solusinya adalah memprogram untuk antarmuka dan menyuntikkan dependensi sehingga objek dapat diuji unit dengan apa pun yang mengimplementasikan antarmuka itu (mis. Mengolok-olok).

Masalahnya adalah Anda harus membuat objek di suatu tempat. Pola Pabrik adalah salah satu cara untuk menggeser kopling dari POXO Anda (Biasa Lama "masukkan bahasa OO Anda di sini" Objek). Jika Anda dan rekan kerja Anda semua menulis kode seperti ini maka wadah IoC adalah "Peningkatan Bertambah" berikutnya yang dapat Anda buat ke basis kode Anda. Ini akan menggeser semua kode boiler Pabrik jahat dari objek bersih dan logika bisnis Anda. Mereka akan mendapatkannya dan menyukainya. Heck, bicarakan perusahaan mengapa Anda menyukainya dan buat semua orang bersemangat.

Jika rekan kerja Anda belum melakukan DI, maka saya sarankan Anda fokus dulu. Sebarkan berita tentang cara menulis kode bersih yang mudah diuji. Bersihkan kode DI adalah bagian yang sulit, begitu Anda berada di sana, memindahkan objek logika kabel dari kelas Pabrik ke wadah IoC harus relatif sepele.

Matt terbakar
sumber
8

Karena semua dependensi terlihat jelas, itu mempromosikan pembuatan komponen yang longgar digabungkan dan pada saat yang sama mudah diakses dan digunakan kembali di seluruh aplikasi.

bbmud
sumber
7

Anda tidak perlu wadah IOC.

Tetapi jika Anda dengan ketat mengikuti pola DI, Anda akan menemukan bahwa memiliki satu akan menghilangkan satu ton kode yang berlebihan dan membosankan.

Itu sering kali merupakan waktu terbaik untuk menggunakan perpustakaan / kerangka kerja - ketika Anda memahami apa yang dilakukannya dan bisa melakukannya tanpa perpustakaan.

Kyoryu
sumber
6

Saya kebetulan sedang dalam proses menarik keluar kode DI rumah tumbuh dan menggantinya dengan IOC. Saya mungkin telah menghapus lebih dari 200 baris kode dan menggantinya dengan sekitar 10. Ya, saya harus melakukan sedikit belajar tentang cara menggunakan wadah (Winsor), tapi saya seorang insinyur yang bekerja pada teknologi internet di Abad 21 jadi saya sudah terbiasa dengan itu. Saya mungkin menghabiskan sekitar 20 menit untuk melihat bagaimana caranya. Ini sepadan dengan waktu saya.

Matt Wrock
sumber
3
"tapi saya seorang insinyur yang bekerja pada teknologi internet di abad ke-21 jadi saya terbiasa dengan itu" -> +1
user96403
5

Ketika Anda terus memisahkan kelas-kelas Anda dan membalikkan dependensi Anda, kelas-kelas terus tetap kecil dan "grafik ketergantungan" terus bertambah besar. (Ini tidak buruk.) Menggunakan fitur dasar wadah IoC membuat kabel semua benda ini sepele, tetapi melakukannya secara manual bisa menjadi sangat memberatkan. Misalnya, bagaimana jika saya ingin membuat instance baru "Foo" tetapi perlu "Bar". Dan "Bar" membutuhkan "A", "B", dan "C". Dan masing-masing dari mereka memerlukan 3 hal lain, dll. (Ya, saya tidak dapat membuat nama palsu yang bagus :)).

Menggunakan wadah IoC untuk membuat grafik objek Anda mengurangi kompleksitas satu ton dan mendorongnya ke dalam konfigurasi satu kali. Saya hanya mengatakan "buatkan saya 'Foo'" dan itu mencari tahu apa yang diperlukan untuk membangun satu.

Beberapa orang menggunakan wadah IoC untuk infrastruktur yang jauh lebih banyak, yang bagus untuk skenario tingkat lanjut, tetapi dalam kasus itu saya setuju itu dapat mengaburkan dan membuat kode sulit dibaca dan debug untuk pengembang baru.

Aaron Lerch
sumber
1
mengapa kita terus menenangkan denominator umum terendah daripada bekerja untuk menarik mereka?
stevenharman
4
Saya tidak mengatakan apa pun tentang memenuhi tuntutan. Saya hanya mengatakan bahwa skenario lanjutan dapat membuat hal-hal lebih membingungkan bagi pengembang baru - ini adalah fakta. Saya tidak mengatakan "jadi jangan lakukan itu". Tetapi bahkan kemudian (waktu untuk advokat iblis) sering ada cara lain untuk mencapai hal yang sama yang membuat basis kode lebih eksplisit dan mudah didekati. Menyembunyikan kompleksitas dalam konfigurasi khusus alat infrastruktur Anda hanya karena Anda bukan alasan yang tepat, dan itu tidak menarik siapa pun.
Aaron Lerch
4

Dittos tentang Persatuan. Menjadi terlalu besar, dan Anda dapat mendengar derit di kasau.

Tidak pernah mengejutkan saya ketika orang-orang mulai menyemburkan tentang bagaimana kode IoC terlihat bersih adalah jenis yang sama dari orang-orang yang pada suatu waktu berbicara tentang bagaimana templat dalam C ++ adalah cara elegan untuk kembali di tahun 90-an, namun saat ini akan mengutuk mereka sebagai sesuatu yang misterius. . Bah!

M Lopez
sumber
2

Di dunia .NET AOP tidak terlalu populer, jadi untuk DI framework adalah satu-satunya pilihan nyata Anda, apakah Anda menulis sendiri atau menggunakan framework lain.

Jika Anda menggunakan AOP, Anda dapat menyuntikkan ketika Anda mengkompilasi aplikasi Anda, yang lebih umum di Jawa.

Ada banyak manfaat untuk DI, seperti pengurangan kopling sehingga pengujian unit lebih mudah, tetapi bagaimana Anda akan menerapkannya? Apakah Anda ingin menggunakan refleksi untuk melakukannya sendiri?

James Black
sumber
Kerangka kerja MEF yang baru memungkinkan hal ini untuk .NET dan saya harus mengatakan bahwa kodenya lebih bersih dan mudah dipahami, membuatnya jauh lebih mudah untuk memahami konsep DI.
terjetyl
4
@TT: MEF bukan wadah injeksi ketergantungan dan tidak ada hubungannya dengan AOP.
Mauricio Scheffer
2

Jadi, sudah hampir 3 tahun, kan?

  1. 50% dari mereka yang berkomentar mendukung kerangka kerja IoC tidak memahami perbedaan antara kerangka kerja IoC dan IoC. Saya ragu mereka tahu bahwa Anda dapat menulis kode tanpa disebarkan ke server aplikasi

  2. Jika kita mengambil kerangka Java Spring paling populer, itu adalah konfigurasi IoC yang dipindahkan dari XMl ke dalam kode dan sekarang terlihat seperti ini

`@Configation public class AppConfig {

public @Bean TransferService transferService() {
    return new TransferServiceImpl(accountRepository());
}

public @Bean AccountRepository accountRepository() {
    return new InMemoryAccountRepository();
}

} `Dan kita perlu kerangka kerja untuk melakukan ini mengapa tepatnya?

Ivan
sumber
1

Jujur saya tidak menemukan ada banyak kasus di mana wadah IoC diperlukan, dan sebagian besar waktu, mereka hanya menambah kompleksitas yang tidak dibutuhkan.

Jika Anda menggunakannya hanya untuk membuat konstruksi objek lebih sederhana, saya harus bertanya, apakah Anda membuat instance objek ini di lebih dari satu lokasi? Apakah lajang tidak sesuai dengan kebutuhan Anda? Apakah Anda mengubah konfigurasi saat runtime? (Mengalihkan tipe sumber data, dll).

Jika ya, maka Anda mungkin perlu wadah IOC. Jika tidak, maka Anda hanya memindahkan inisialisasi dari tempat pengembang dapat dengan mudah melihatnya.

Siapa bilang antarmuka lebih baik daripada warisan? Katakanlah Anda sedang menguji Layanan. Mengapa tidak menggunakan konstruktor DI, dan membuat ejekan dependensi menggunakan warisan? Sebagian besar layanan yang saya gunakan hanya memiliki sedikit ketergantungan. Melakukan pengujian unit dengan cara ini mencegah pemeliharaan satu ton antarmuka yang tidak berguna dan berarti Anda tidak harus menggunakan Resharper untuk dengan cepat menemukan deklarasi suatu metode.

Saya percaya bahwa untuk sebagian besar implementasi, mengatakan bahwa IoC Containers menghapus kode yang tidak dibutuhkan adalah mitos.

Pertama, ada pengaturan wadah di tempat pertama. Maka Anda masih harus mendefinisikan setiap objek yang perlu diinisialisasi. Jadi Anda tidak menyimpan kode dalam inisialisasi, Anda memindahkannya (kecuali objek Anda digunakan lebih dari sekali. Apakah lebih baik sebagai Singleton?). Kemudian, untuk setiap objek yang Anda inisialisasi dengan cara ini, Anda harus membuat dan memelihara antarmuka.

Adakah yang punya pemikiran tentang ini?

Fred
sumber
Bahkan untuk proyek-proyek kecil, menempatkan konfigurasi dalam XML membuatnya jauh lebih bersih & lebih modular - dan untuk pertama kalinya, membuat kode dapat digunakan kembali (karena tidak lagi terkontaminasi dengan lem). Untuk produk perangkat lunak yang tepat, Anda dapat mengonfigurasi atau bertukar misalnya ShippingCostCalculatoratau ProductUIPresentation, atau strategi / implementasi lainnya, dalam basis kode yang sama persis , dengan menggunakan hanya satu file XML yang diubah.
Thomas W
Jika itu diterapkan mau tak mau, sebagai praktik umum, saya pikir Anda menambahkan lebih banyak kompleksitas, tidak kurang. Ketika Anda perlu menukar sesuatu saat runtime, mereka bagus, tetapi mengapa menambahkan kebingungan? Mengapa menjaga overhead antarmuka tambahan untuk hal-hal yang tidak akan pernah padam? Saya tidak mengatakan jangan gunakan IOC untuk pengujian. Dengan segala cara, gunakan properti atau bahkan injeksi ketergantungan konstruktor. Apakah Anda perlu menggunakan antarmuka? Mengapa tidak mensubclass objek pengujian Anda? Bukankah itu lebih bersih?
Fred
Anda tidak perlu antarmuka sama sekali, untuk melakukan konfigurasi XML / IoC. Saya menemukan peningkatan besar dalam kejelasan dalam proyek kecil dengan hanya ~ 8 halaman dan 5 atau 6 layanan. Ada sedikit kebingungan karena layanan & pengontrol tidak lagi membutuhkan kode lem.
Thomas W
1

Anda akan membutuhkan wadah IoC jika Anda perlu memusatkan konfigurasi dependensi Anda sehingga mereka dapat dengan mudah diganti secara massal. Ini paling masuk akal dalam TDD, di mana banyak dependensi ditukar, tetapi di mana ada sedikit saling ketergantungan antara dependensi. Hal ini dilakukan pada biaya yang mengaburkan aliran kontrol konstruksi objek sampai taraf tertentu, sehingga memiliki konfigurasi yang terorganisir dengan baik dan didokumentasikan secara wajar adalah penting. Ini juga baik untuk memiliki alasan untuk melakukan ini, jika tidak, itu hanyalah abstraksi penyepuhan emas . Saya telah melihat itu dilakukan dengan sangat buruk sehingga diseret menjadi setara dengan pernyataan goto untuk konstruktor.

DaveMorganTexas
sumber
1

Inilah sebabnya. Proyek ini disebut IOC-with-Ninject. Anda dapat mengunduh dan menjalankannya dengan Visual Studio. Contoh ini menggunakan Ninject tetapi SEMUA pernyataan 'baru' ada di satu lokasi dan Anda dapat sepenuhnya mengubah cara aplikasi Anda berjalan dengan mengubah modul bind mana yang akan digunakan. Contohnya diatur sehingga Anda dapat mengikat ke versi layanan yang diejek atau versi nyata. Dalam proyek-proyek kecil yang mungkin tidak masalah, tetapi dalam proyek-proyek besar itu adalah masalah besar.

Supaya jelas, keuntungan seperti yang saya lihat: 1) SEMUA pernyataan baru di satu lokasi di root kode. 2) Benar-benar kode faktor ulang dengan satu perubahan. 3) Poin ekstra untuk 'faktor keren' karena itu ... well: keren. : p

CodeChops
sumber
1

Saya akan mencoba mencari tahu mengapa IOC mungkin tidak baik dari sudut pandang saya.

Seperti yang lainnya, wadah IOC (atau seperti yang dikatakan Einstein, I = OC ^ 2) adalah konsep yang harus Anda putuskan sendiri apakah Anda membutuhkannya atau tidak dalam kode Anda. Teriakan mode terbaru tentang IOC hanya itu, mode. Jangan jatuh cinta pada mode, itu yang pertama. Ada banyak sekali konsep di luar sana yang bisa Anda terapkan dalam kode Anda. Pertama-tama, saya menggunakan injeksi dependensi sejak saya memulai pemrograman, dan mempelajari istilah itu sendiri ketika dipopulerkan dengan nama itu. Kontrol ketergantungan adalah subjek yang sangat tua dan telah ditangani sejauh ini dalam triliunan cara, tergantung pada apa yang dipisahkan dari apa. Memisahkan segala sesuatu dari segalanya adalah omong kosong. Masalah dengan wadah IOC adalah bahwa ia mencoba untuk menjadi sama bermanfaatnya dengan Entity Framework atau NHibernate. Sementara menulis mapper-objek relasional hanyalah suatu keharusan segera setelah Anda harus memasangkan setiap database dengan sistem Anda, wadah IOC tidak selalu diperlukan. Jadi ketika wadah IOC berguna:

  1. Ketika Anda memiliki situasi dengan banyak dependensi yang ingin Anda kelola
  2. Ketika Anda tidak peduli tentang menggabungkan kode Anda dengan produk pihak ketiga
  3. Saat pengembang Anda ingin mempelajari cara bekerja dengan alat baru

1: Ini tidak sering bahwa Anda memiliki begitu banyak dependensi dalam kode Anda, atau bahwa Anda menyadarinya sejak awal desain. Berpikir abstrak berguna ketika berpikir abstrak adalah karena.

2: Menggabungkan kode Anda dengan kode pihak ketiga adalah masalah HuGe. Saya bekerja dengan kode yang berumur 10+ tahun dan pada saat itu mengikuti konsep mewah dan canggih ATL, COM, COM + dan seterusnya. Tidak ada yang dapat Anda lakukan dengan kode itu sekarang. Apa yang saya katakan adalah bahwa konsep lanjutan memberikan keuntungan yang jelas, namun ini dibatalkan pada akhirnya dengan keuntungan yang sudah usang itu sendiri. Itu hanya membuat semuanya lebih mahal.

3: Pengembangan perangkat lunak cukup sulit. Anda dapat memperluasnya ke tingkat yang tidak dapat dikenali jika Anda mengizinkan beberapa konsep lanjutan untuk memotong kode Anda. Ada masalah dengan IOC2. Meskipun dependensi decoupling, itu decoupling aliran logika juga. Bayangkan Anda telah menemukan bug dan Anda perlu istirahat untuk memeriksa situasinya. IOC2, seperti konsep lanjutan lainnya, membuatnya semakin sulit. Memperbaiki bug dalam konsep lebih sulit daripada memperbaiki bug dalam kode yang lebih jelas, karena ketika Anda memperbaiki bug konsep harus dipatuhi lagi. (Hanya untuk memberi Anda contoh, C ++. NET terus-menerus mengubah sintaks sehingga Anda harus berpikir keras sebelum Anda refactor beberapa versi .NET.) Jadi, apa masalah dengan IOC? Masalahnya adalah dalam menyelesaikan dependensi. Logika untuk menyelesaikan umumnya tersembunyi di IOC2 itu sendiri, ditulis mungkin dengan cara tidak biasa yang perlu Anda pelajari dan pelihara. Apakah produk pihak ketiga Anda akan ada di sana dalam 5 tahun? Microsoft tidak.

Sindrom "Kami tahu bagaimana" ditulis di semua tempat mengenai IOC2. Ini mirip dengan pengujian otomatisasi. Istilah mewah dan solusi sempurna pada pandangan pertama, Anda cukup melakukan semua tes untuk dieksekusi pada malam hari dan melihat hasilnya di pagi hari. Sangat menyakitkan untuk menjelaskan perusahaan demi perusahaan apa arti pengujian otomatis sebenarnya. Pengujian otomatis jelas bukan cara cepat untuk mengurangi jumlah bug yang dapat Anda perkenalkan semalam untuk meningkatkan kualitas produk Anda. Tapi, fesyen membuat gagasan itu dominan dan mengganggu. IOC2 menderita sindrom yang sama. Dipercaya bahwa Anda perlu mengimplementasikannya agar perangkat lunak Anda menjadi baik. Setiap wawancara baru-baru ini saya ditanya apakah saya menerapkan IOC2 dan otomatisasi. Itu adalah tanda mode: perusahaan memiliki beberapa bagian kode yang ditulis dalam MFC yang tidak akan mereka tinggalkan.

Anda perlu mempelajari IOC2 seperti konsep lain dalam perangkat lunak. Keputusan jika IOC2 perlu digunakan ada di dalam tim dan perusahaan. Namun, setidaknya SEMUA argumen di atas harus disebutkan sebelum keputusan dibuat. Hanya jika Anda melihat sisi positifnya melebihi sisi negatifnya, Anda dapat membuat keputusan positif.

Tidak ada yang salah dengan IOC2 kecuali bahwa itu hanya menyelesaikan masalah yang dipecahkan dan memperkenalkan masalah yang diperkenalkannya. Tidak ada lagi. Namun, menentang mode sangat sulit, mereka memiliki mulut yang berkeringat, pengikut apa pun. Sungguh aneh bagaimana tidak satu pun dari mereka ada di sana ketika masalah dengan keanggunan mereka menjadi jelas. Banyak konsep dalam industri perangkat lunak telah dipertahankan karena mereka menghasilkan laba, buku ditulis, konferensi diadakan, produk baru dibuat. Itu fashion, biasanya berumur pendek. Begitu orang menemukan sesuatu yang lain, mereka meninggalkannya sepenuhnya. IOC2 berguna tetapi menunjukkan tanda-tanda yang sama seperti banyak konsep lenyap lainnya yang pernah saya lihat. Saya tidak tahu apakah itu akan bertahan. Tidak ada aturan untuk itu. Anda pikir jika itu berguna, itu akan bertahan. Tidak, tidak seperti itu. Satu perusahaan kaya besar sudah cukup dan konsepnya bisa mati dalam beberapa minggu. Kita lihat saja nanti. NHibernate selamat, EF berada di urutan kedua. Mungkin IOC2 akan bertahan juga. Jangan lupa bahwa sebagian besar konsep dalam pengembangan perangkat lunak adalah tentang tidak ada yang istimewa, mereka sangat logis, sederhana dan jelas, dan kadang-kadang lebih sulit untuk mengingat konvensi penamaan saat ini daripada memahami konsep itu sendiri. Apakah pengetahuan IOC2 membuat pengembang menjadi pengembang yang lebih baik? Tidak, karena jika pengembang tidak dapat membuat konsep yang mirip dengan IOC2 maka akan sulit baginya untuk memahami masalah yang diselesaikan oleh IOC2, menggunakannya akan terlihat buatan dan dia mungkin mulai menggunakannya demi menjadi semacam politik yang benar. Jangan lupa bahwa sebagian besar konsep dalam pengembangan perangkat lunak adalah tentang tidak ada yang istimewa, mereka sangat logis, sederhana dan jelas, dan kadang-kadang lebih sulit untuk mengingat konvensi penamaan saat ini daripada memahami konsep itu sendiri. Apakah pengetahuan IOC2 membuat pengembang menjadi pengembang yang lebih baik? Tidak, karena jika pengembang tidak dapat membuat konsep yang mirip dengan IOC2 maka akan sulit baginya untuk memahami masalah yang diselesaikan oleh IOC2, menggunakannya akan terlihat buatan dan dia mungkin mulai menggunakannya demi menjadi semacam politik yang benar. Jangan lupa bahwa sebagian besar konsep dalam pengembangan perangkat lunak adalah tentang tidak ada yang istimewa, mereka sangat logis, sederhana dan jelas, dan kadang-kadang lebih sulit untuk mengingat konvensi penamaan saat ini daripada memahami konsep itu sendiri. Apakah pengetahuan IOC2 membuat pengembang menjadi pengembang yang lebih baik? Tidak, karena jika pengembang tidak dapat membuat konsep yang mirip dengan IOC2 maka akan sulit baginya untuk memahami masalah yang diselesaikan oleh IOC2, menggunakannya akan terlihat buatan dan dia mungkin mulai menggunakannya demi menjadi semacam politik yang benar. Apakah pengetahuan IOC2 membuat pengembang menjadi pengembang yang lebih baik? Tidak, karena jika pengembang tidak dapat membuat konsep yang mirip dengan IOC2 maka akan sulit baginya untuk memahami masalah yang diselesaikan oleh IOC2, menggunakannya akan terlihat buatan dan dia mungkin mulai menggunakannya demi menjadi semacam politik yang benar. Apakah pengetahuan IOC2 membuat pengembang menjadi pengembang yang lebih baik? Tidak, karena jika pengembang tidak dapat membuat konsep yang mirip dengan IOC2 maka akan sulit baginya untuk memahami masalah yang diselesaikan oleh IOC2, menggunakannya akan terlihat buatan dan dia mungkin mulai menggunakannya demi menjadi semacam politik yang benar.

pengguna1228608
sumber
0

Secara pribadi, saya menggunakan IoC sebagai semacam peta struktur aplikasi saya (Ya, saya juga lebih suka StructureMap;)). Itu membuatnya mudah untuk menggantikan implementasi antarmuka biasa saya dengan implementasi Moq selama tes. Membuat setup tes bisa semudah membuat init-call baru ke IoC-framework saya, menggantikan kelas mana saja yang merupakan batas-test saya dengan mock.

Ini mungkin bukan untuk apa IoC ada, tapi itulah yang saya temukan paling banyak saya gunakan ..

cwap
sumber
0

Saya menyadari ini posting yang agak lama, tetapi tampaknya masih cukup aktif, dan saya pikir saya akan berkontribusi beberapa poin yang belum disebutkan dalam jawaban lain.

Saya akan mengatakan bahwa saya setuju dengan manfaat injeksi ketergantungan, tetapi saya lebih memilih untuk membangun dan mengelola objek sendiri, menggunakan pola yang tidak seperti yang diuraikan oleh Maxm007 dalam jawabannya. Saya telah menemukan dua masalah utama dengan menggunakan wadah pihak ke-3:

1) Memiliki perpustakaan pihak ke-3 yang mengatur masa pakai objek Anda "secara otomatis" dapat memberikan hasil yang tidak terduga. Kami telah menemukan bahwa terutama dalam proyek besar, Anda dapat memiliki salinan objek yang jauh lebih banyak daripada yang Anda harapkan, dan lebih banyak daripada yang Anda lakukan jika Anda mengelola siklus hidup secara manual. Saya yakin ini bervariasi tergantung pada kerangka yang digunakan, tetapi masalahnya tetap ada. Ini juga bisa menjadi masalah jika objek Anda menyimpan sumber daya, koneksi data, dll., Karena objek kadang-kadang bisa hidup lebih lama dari yang Anda harapkan. Jadi mau tidak mau, wadah IoC cenderung meningkatkan pemanfaatan sumber daya dan jejak memori aplikasi.

2) Kontainer IoC, menurut pendapat saya, adalah bentuk "pemrograman kotak hitam". Saya telah menemukan bahwa khususnya, pengembang kami yang kurang berpengalaman cenderung menyalahgunakan mereka. Hal ini memungkinkan programmer untuk tidak harus berpikir tentang bagaimana benda harus berhubungan satu sama lain atau bagaimana memisahkan mereka, karena itu memberi mereka mekanisme di mana mereka dapat dengan mudah mengambil benda apa pun yang mereka inginkan dari udara tipis. Misalnya, mungkin ada alasan desain yang baik bahwa ObjectA seharusnya tidak pernah tahu tentang ObjectB secara langsung, tetapi daripada membuat pabrik atau jembatan atau pencari layanan, programmer yang tidak berpengalaman hanya akan mengatakan "tidak masalah, saya hanya akan mengambil ObjectB dari wadah IoC ". Ini benar-benar dapat menyebabkan peningkatan kopling objek, yang seharusnya membantu mencegah IoC.

amnesia
sumber
-8

Ketergantungan Injeksi dalam proyek ASP.NET dapat diselesaikan dengan beberapa baris kode. Saya kira ada beberapa keuntungan menggunakan wadah ketika Anda memiliki aplikasi yang menggunakan beberapa ujung depan dan membutuhkan pengujian unit.

etechpartner
sumber
1
dapatkah kamu memberi contoh Bagaimana DI berbeda di ASP.NET dibandingkan dengan jenis aplikasi lainnya?
tofi9