Apakah Injeksi Ketergantungan Orang Miskin adalah cara yang baik untuk memperkenalkan kemampuan uji ke aplikasi warisan?

14

Pada tahun lalu, saya menciptakan sistem baru menggunakan Dependency Injection dan wadah IOC. Ini mengajari saya banyak hal tentang DI!

Namun, bahkan setelah mempelajari konsep dan pola yang tepat, saya menganggapnya sebagai tantangan untuk memisahkan kode dan memperkenalkan wadah IOC ke dalam aplikasi warisan. Aplikasi ini cukup besar sehingga implementasi yang benar akan luar biasa. Bahkan jika nilainya dipahami dan waktu diberikan. Siapa yang diberikan waktu untuk hal seperti ini ??

Tujuannya tentu saja adalah untuk membawa unit test ke logika bisnis!
Logika bisnis yang terkait dengan panggilan basis data pencegah pengujian.

Saya telah membaca artikel-artikel dan saya memahami bahaya Injeksi Ketergantungan Orang Miskin seperti yang dijelaskan dalam artikel Los Techies ini . Saya mengerti itu tidak benar - benar memisahkan apa pun.
Saya mengerti bahwa ini dapat melibatkan banyak refactoring seluruh sistem karena implementasi memerlukan dependensi baru. Saya tidak akan mempertimbangkan menggunakannya pada proyek baru dengan ukuran berapa pun jumlahnya.

Pertanyaan: Apakah saya tetap bisa menggunakan DIA Poor Man untuk memperkenalkan testability ke aplikasi lawas dan memulai penggulingan bola?

Selain itu, apakah menggunakan DIA Orang Miskin sebagai pendekatan akar rumput untuk Injeksi Ketergantungan sejati merupakan cara yang berharga untuk mendidik tentang kebutuhan dan manfaat prinsip?

Bisakah Anda refactor metode yang memiliki ketergantungan panggilan database dan abstrak yang memanggil ke belakang antarmuka? Cukup memiliki abstraksi itu akan membuat metode itu dapat diuji karena implementasi tiruan dapat diteruskan melalui konstruktor yang berlebihan.

Di ujung jalan, begitu upaya mendapatkan pendukung, proyek dapat diperbarui untuk menerapkan wadah IOC dan konstruktor akan berada di luar sana yang mengambil abstraksi.

Airn5475
sumber
2
Hanya sebuah catatan. I consider it a challenge to decouple code and introduce an IOC container into a legacy applicationtentu saja itu. Ini bernama utang teknis. Inilah sebabnya mengapa sebelum ada perombakan besar, lebih disukai refaktor kecil dan kontinu. Mengurangi kelemahan desain utama dan pindah ke IoC akan lebih sulit.
Laiv

Jawaban:

25

Kritik tentang Poor Man's Injection di NerdDinner kurang berkaitan dengan apakah Anda menggunakan Container DI atau tidak daripada mengatur kelas Anda dengan benar.

Dalam artikel itu, mereka menyatakan itu

public class SearchController : Controller {

    IDinnerRepository dinnerRepository;

    public SearchController() : this(new DinnerRepository()) { }

    public SearchController(IDinnerRepository repository) {
        dinnerRepository = repository;
    }
}

tidak benar karena, sementara konstruktor pertama memang menyediakan mekanisme mundur yang nyaman untuk membangun kelas, itu juga menciptakan ketergantungan yang terikat erat DinnerRepository.

Obat yang benar tentu saja bukan, seperti yang disarankan Los Techies, untuk menambahkan wadah DI, melainkan untuk menghapus konstruktor yang menyinggung.

public class SearchController : Controller 
{
    IDinnerRepository dinnerRepository;

    public SearchController(IDinnerRepository repository) {
        dinnerRepository = repository;
    }
}

Kelas yang tersisa sekarang memiliki dependensi yang terbalik dengan benar. Anda sekarang bebas untuk menyuntikkan dependensi tersebut sesuka Anda.

Robert Harvey
sumber
Terima kasih atas pemikiran anda! Saya mengerti "konstruktor yang menyinggung" dan bagaimana kita harus menghindarinya. Saya mengerti bahwa memiliki ketergantungan ini tidak memisahkan apa pun, tetapi TIDAK memungkinkan untuk pengujian unit. Saya pada awalnya memperkenalkan DI dan mendapatkan unit test secepat mungkin. Saya mencoba untuk menghindari komplikasi dari DI / IOC Container atau pabrik di awal permainan ini. Seperti yang disebutkan, nanti ketika dukungan untuk DI bertambah, kita bisa mengimplementasikan Kontainer dan menghapus "konstruktor yang menyinggung" itu.
Airn5475
Konstruktor default tidak diperlukan untuk pengujian unit. Anda dapat menyerahkan dependensi apa pun yang Anda suka ke kelas tanpa itu, termasuk objek tiruan.
Robert Harvey
2
@ Airn5475 berhati-hati bahwa Anda tidak lebih abstrak juga. Tes-tes tersebut harus menguji perilaku yang bermakna - menguji bahwa suatu XServiceparameter meneruskan ke XRepositorydan mengembalikan apa yang didapatnya kembali tidak terlalu berguna bagi siapa pun dan juga tidak memberi Anda banyak hal dalam ekstensibilitas. Tulis pengujian unit Anda untuk menguji perilaku yang bermakna dan pastikan komponen Anda tidak terlalu sempit sehingga Anda benar-benar memindahkan semua logika Anda ke root komposisi Anda.
Ant P
Saya tidak mengerti bagaimana memasukkan konstruktor yang menyinggung akan membantu untuk unit test, tentu saja itu yang terjadi? Hapus konstruktor yang menyinggung sehingga DI diimplementasikan dengan benar dan lulus dalam ketergantungan mengejek ketika Anda instantiating objek.
Rob
@Rob: Tidak. Ini hanya cara yang nyaman bagi konstruktor default untuk berdiri objek yang valid.
Robert Harvey
16

Anda membuat asumsi yang salah di sini tentang apa "DI orang miskin".

Menciptakan kelas yang memiliki konstruktor "pintas" yang masih membuat kopling bukanlah DIM orang miskin.

Tidak menggunakan wadah, dan membuat semua suntikan dan pemetaan secara manual, adalah DI orang miskin.

Istilah "DIA orang miskin" terdengar seperti hal yang buruk untuk dilakukan. Untuk alasan itu, istilah "DI murni" dianjurkan akhir-akhir ini karena kedengarannya lebih positif dan sebenarnya lebih akurat menggambarkan prosesnya.

Tidak hanya itu benar-benar baik untuk menggunakan DI orang miskin / murni untuk memperkenalkan DI ke dalam aplikasi yang sudah ada, itu juga merupakan cara yang valid untuk menggunakan DI untuk banyak aplikasi baru. Dan seperti yang Anda katakan, setiap orang harus menggunakan DI murni pada setidaknya satu proyek untuk benar-benar memahami cara kerja DI, sebelum menangani tanggung jawab atas "keajaiban" wadah IoC.

David Arno
sumber
8
DI "Poor man's" atau "Pure", apa pun yang Anda inginkan, juga mengurangi banyak masalah dengan wadah IoC. Saya dulu menggunakan wadah IoC untuk semuanya tetapi semakin banyak waktu berlalu semakin saya lebih suka menghindari mereka sama sekali.
Ant P
7
@AntP, aku bersamamu: Aku 100% murni DI hari ini dan secara aktif menghindari wadah. Tapi saya (kami) berada di minoritas pada skor itu sejauh yang saya tahu.
David Arno
2
Tampak sangat sedih - ketika saya menjauh dari kamp "ajaib" wadah IoC, mengejek perpustakaan dan sebagainya dan menemukan diri saya merangkul OOP, enkapsulasi, tes linting ganda dan verifikasi negara, tampaknya tren sihir masih ada di naik. Tetap memberi +1.
Semut P
3
@AntP & David: Senang mendengar saya tidak sendirian di kamp 'DI Murni'. Wadah DI dibuat untuk mengatasi kekurangan dalam bahasa. Mereka menambah nilai dalam arti itu. Tetapi dalam pikiran saya, jawaban sebenarnya adalah untuk memperbaiki bahasa (atau pindah ke bahasa lain) dan tidak membangun cruft di atasnya.
JimmyJames
1

Pergeseran paragid di tim legacy / basis kode sangat berisiko:

Setiap kali Anda menyarankan "perbaikan" ke kode warisan dan ada programer "warisan" di tim, Anda hanya memberi tahu semua orang bahwa "mereka melakukan kesalahan" dan Anda menjadi Musuh untuk sisa waktu Anda bersama perusahaan.

Menggunakan Framework DI sebagai palu untuk menghancurkan semua kuku hanya akan membuat kode warisan lebih buruk daripada yang lebih baik dalam semua kasus. Ini juga sangat berisiko secara pribadi.

Bahkan dalam kasus-kasus yang paling terbatas seperti hanya sehingga dapat digunakan dalam kasus uji hanya akan membuat kasus-kasus uji "non-standar" dan "asing" kode yang paling baik hanya akan ditandai @Ignoreketika mereka rusak atau lebih buruk lagi terus-menerus dikeluhkan programmer lama dengan pengaruh paling kuat dengan manajemen dan ditulis ulang "dengan benar" dengan semua "waktu yang terbuang untuk tes unit" ini menyalahkan Anda.

Memperkenalkan kerangka kerja DI, atau bahkan konsep "DI Murni" ke lini aplikasi bisnis, apalagi basis kode warisan yang besar tanpa campur tangan manajemen, tim dan terutama sponsor dari pengembang utama hanya akan menjadi lonceng kematian untuk Anda secara sosial dan politik di tim / perusahaan. Melakukan hal-hal seperti ini sangat berisiko dan dapat menjadi bunuh diri secara politis dengan cara terburuk.

Ketergantungan Injeksi adalah solusi mencari masalah.

Bahasa apa pun dengan konstruktor menurut definisi menggunakan injeksi ketergantungan dengan konvensi jika Anda tahu apa yang Anda lakukan dan memahami cara menggunakan konstruktor dengan benar, ini hanya desain yang bagus.

Injeksi ketergantungan hanya berguna dalam dosis kecil untuk rentang yang sangat sempit:

  • Hal-hal yang banyak berubah atau memiliki banyak implementasi alternatif yang terikat secara statis.

    • Driver JDBC adalah contoh sempurna.
    • Klien HTTP yang mungkin berbeda dari satu platform ke platform lainnya.
    • Sistem logging yang bervariasi berdasarkan platform.
  • Sistem plugin yang memiliki plugin yang dapat dikonfigurasi yang dapat didefinisikan dalam kode konfigurasi dalam kerangka kerja Anda dan ditemukan secara otomatis saat startup dan dimuat / dimuat secara dinamis saat program sedang berjalan.


sumber
10
Aplikasi pembunuh untuk DI adalah pengujian unit. Seperti yang telah Anda tunjukkan, sebagian besar perangkat lunak tidak akan membutuhkan banyak DI dengan sendirinya. Tetapi tes juga merupakan klien dari kode ini, jadi kita harus merancang untuk testabilitas. Secara khusus, ini berarti menempatkan jahitan dalam desain kami di mana tes dapat mengamati perilaku. Ini tidak memerlukan kerangka DI mewah, tetapi memang mengharuskan dependensi yang relevan dibuat eksplisit dan dapat diganti.
amon
4
Jika kami para devs tahu jika maju apa yang bisa berubah, itu akan menjadi setengah pertempuran. Saya telah mengalami perkembangan panjang di mana banyak hal yang seharusnya "diperbaiki" berubah. Pemeriksaan kedepan di sana-sini menggunakan DI bukanlah hal yang buruk. Menggunakannya di mana-mana sepanjang waktu menampar pemrograman pemujaan kargo.
Robbie Dee
pengujian yang terlalu rumit hanya akan membuat mereka basi dan ditandai diabaikan alih-alih diperbarui di masa depan. DI dalam kode lama adalah ekonomi palsu. Semua yang akan mereka lakukan adalah menjadikan Anda "orang jahat" karena meletakkan semua "kode non-standar, terlalu rumit ini yang tidak dipahami oleh siapa pun" di basis kode. Anda telah diperingatkan.
4
@JarrodRoberson, itu benar-benar lingkungan yang beracun. Salah satu tanda kunci dari pengembang yang baik adalah bahwa mereka melihat kembali kode yang mereka tulis di masa lalu dan berpikir, "Aduh, apakah saya benar-benar menulis itu? Itu sangat salah!" Itu karena mereka telah tumbuh sebagai pengembang dan belajar hal-hal baru. Seseorang yang melihat kode yang mereka tulis lima tahun yang lalu dan melihat tidak ada yang salah dengan kode itu tidak belajar apa pun dalam lima tahun itu. Jadi, jika memberi tahu orang-orang bahwa mereka melakukan kesalahan di masa lalu membuat Anda menjadi musuh, maka larilah dari perusahaan itu dengan cepat, karena mereka adalah tim buntu yang akan menahan Anda dan menurunkan Anda ke level yang buruk.
David Arno
1
Tidak yakin saya setuju dengan hal-hal orang jahat tetapi bagi saya kunci dari ini adalah bahwa Anda tidak perlu DI untuk melakukan pengujian unit. Menerapkan DI untuk membuat kode lebih dapat diuji hanya memperbaiki masalah.
Semut