Apa sebenarnya Injeksi Lapangan dan bagaimana menghindarinya?

130

Saya membaca di beberapa posting tentang Spring MVC dan Portlet bahwa injeksi lapangan tidak disarankan. Seperti yang saya pahami, injeksi lapangan adalah saat Anda menyuntikkan Bean dengan @Autowiredseperti ini:

@Component
public class MyComponent {
    @Autowired
    private Cart cart;
}

Selama penelitian saya, saya juga membaca tentang injeksi konstruktor :

@Component
public class MyComponent {
    private final Cart cart;

    @Autowired
    public MyComponent(Cart cart){
       this.cart = cart;
    }
}

Apa keuntungan dan kerugian dari kedua jenis suntikan ini?


EDIT 1: Karena pertanyaan ini ditandai sebagai duplikat dari pertanyaan ini, saya memeriksanya. Karena tidak ada contoh kode apa pun baik dalam pertanyaan maupun jawaban, tidak jelas bagi saya apakah saya benar dengan tebakan saya jenis injeksi yang saya gunakan.

T. Jung
sumber
3
Jika injeksi lapangan seburuk yang Anda gambarkan, mengapa Spring mengizinkannya? Injeksi lapangan memiliki keuntungan tersendiri, yaitu membuat kode lebih mudah dibaca dan tidak terlalu bertele-tele. Jika Anda cukup disiplin dalam pengkodean Anda, Anda dapat yakin hal-hal tidak akan rusak bahkan jika Anda menggunakan injeksi lapangan.
abu
@ash Karena itu adalah fitur yang rapi pada saat itu, dan implikasinya tidak sepenuhnya dipikirkan. Alasan yang sama yang Date(int,int,int)ada.
chrylis -cautiouslyoptimistic-

Jawaban:

225

Jenis injeksi

Ada tiga pilihan bagaimana dependensi dapat disuntikkan ke dalam bean:

  1. Melalui konstruktor
  2. Melalui penyetel atau metode lain
  3. Melalui refleksi, langsung ke lapangan

Anda menggunakan opsi 3. Itulah yang terjadi saat Anda menggunakan @Autowiredlangsung di bidang Anda.


Pedoman injeksi

Panduan umum, yang direkomendasikan oleh Spring (lihat bagian DI berbasis Konstruktor atau DI berbasis Setter ) adalah sebagai berikut:

  • Untuk dependensi wajib atau saat bertujuan untuk keabadian, gunakan injeksi konstruktor
  • Untuk ketergantungan opsional atau yang dapat diubah, gunakan injeksi penyetel
  • Hindari injeksi lapangan dalam banyak kasus

Kelemahan injeksi lapangan

Alasan mengapa injeksi lapangan tidak disukai adalah sebagai berikut:

  • Anda tidak dapat membuat objek yang tidak dapat diubah, seperti yang Anda bisa dengan injeksi konstruktor
  • Kelas Anda memiliki kopling yang erat dengan wadah DI Anda dan tidak dapat digunakan di luarnya
  • Kelas Anda tidak dapat dibuat instance-nya (misalnya dalam pengujian unit) tanpa refleksi. Anda memerlukan kontainer DI untuk membuat instance-nya, yang membuat pengujian Anda lebih seperti pengujian integrasi
  • Dependensi nyata Anda disembunyikan dari luar dan tidak tercermin dalam antarmuka Anda (baik konstruktor atau metode)
  • Sangat mudah untuk memiliki sepuluh dependensi. Jika Anda menggunakan injeksi konstruktor, Anda akan memiliki konstruktor dengan sepuluh argumen, yang akan memberi sinyal bahwa ada sesuatu yang mencurigakan. Namun Anda dapat menambahkan kolom yang diinjeksi menggunakan injeksi kolom tanpa batas. Memiliki terlalu banyak dependensi adalah tanda bahaya bahwa kelas biasanya melakukan lebih dari satu hal, dan mungkin melanggar Prinsip Tanggung Jawab Tunggal.

Kesimpulan

Bergantung pada kebutuhan Anda, Anda harus menggunakan injeksi konstruktor atau campuran injeksi konstruktor dan penyetel. Injeksi lapangan memiliki banyak kekurangan dan harus dihindari. Satu-satunya keuntungan dari injeksi lapangan adalah lebih nyaman untuk menulis, yang tidak melebihi semua kekurangannya.


Bacaan lebih lanjut

Saya menulis artikel blog tentang mengapa injeksi lapangan biasanya tidak disarankan: Injeksi Ketergantungan Lapangan Dianggap Berbahaya .

Vojtech Ruzicka
sumber
12
Secara umum bukanlah ide yang baik dan tidak baik untuk memberitahu dunia "injeksi lapangan harus dihindari". Tunjukkan pro dan kontra dan biarkan orang lain memutuskan sendiri;) Banyak orang memiliki pengalaman dan cara lain dalam memandang sesuatu.
pelaku diet
6
Itu mungkin kasusnya di sini, tetapi ada kasus lain di mana komunitas telah mencapai konsensus umum untuk mencegah sesuatu. Ambil, misalnya, Notasi Hongaria.
Jannik
Anda memberikan beberapa poin bagus sebagai visibilitas testabilitas dan ketergantungan tetapi saya tidak setuju dengan semua. Injeksi konstruktor tidak memiliki kekurangan? Memiliki 5 atau 6 field untuk dimasukkan ke dalam kelas yang melakukan komposisi panggilan yang sebenarnya mungkin diinginkan. Saya juga tidak setuju dengan Anda dengan kekekalan. Memiliki bidang akhir tidak wajib memiliki kelas yang tidak dapat diubah. Itu lebih disukai. Yang mana sangat berbeda.
davidxxx
Saya pikir yang Anda maksud adalah "Untuk ketergantungan wajib atau saat bertujuan untuk keabadian "
Alex Terreaux
1
Saya merujuk ke tautan di awal jawaban yang tertaut ke dokumen musim semi
Vojtech Ruzicka
47

Ini adalah salah satu diskusi yang tidak pernah berakhir dalam pengembangan perangkat lunak, tetapi pemberi pengaruh utama dalam industri ini semakin banyak berpendapat tentang topik tersebut dan mulai menyarankan injeksi konstruktor sebagai opsi yang lebih baik.

Injeksi konstruktor

Kelebihan:

  • Testabilitas yang lebih baik . Anda tidak memerlukan pustaka tiruan atau konteks Spring dalam pengujian unit. Anda dapat membuat objek yang ingin Anda uji dengan kata kunci baru . Tes semacam itu selalu lebih cepat karena tidak bergantung pada mekanisme refleksi. ( Pertanyaan ini ditanyakan 30 menit kemudian. Jika penulis telah menggunakan injeksi konstruktor tidak akan muncul).
  • Kekekalan . Setelah dependensi ditetapkan, mereka tidak dapat diubah.
  • Kode yang lebih aman . Setelah menjalankan konstruktor, objek Anda siap digunakan karena Anda dapat memvalidasi apa pun yang diteruskan sebagai parameter. Objek bisa siap atau tidak, tidak ada status di antaranya. Dengan injeksi lapangan Anda memperkenalkan langkah perantara saat objek rapuh.
  • Ekspresi yang lebih bersih dari dependensi wajib . Injeksi lapangan tidak jelas dalam hal ini.
  • Membuat developer memikirkan desainnya . dit menulis tentang sebuah konstruktor dengan 8 parameter, yang sebenarnya merupakan tanda desain yang buruk dan objek Tuhan anti-pola . Tidak masalah apakah kelas memiliki 8 dependensi dalam konstruktornya atau dalam bidang, itu selalu salah. Orang lebih enggan menambahkan lebih banyak dependensi ke konstruktor daripada melalui kolom. Ini berfungsi sebagai sinyal ke otak Anda bahwa Anda harus berhenti sejenak dan memikirkan struktur kode Anda.

Kekurangan:

  • Lebih banyak kode (tetapi IDE modern mengurangi rasa sakit).

Pada dasarnya, injeksi lapangan adalah kebalikannya.

Daniel Olszewski
sumber
1
testability, ya, itu adalah mimpi buruk bagi saya untuk mengejek kacang yang diinjeksi lapangan. Sekali, saya menggunakan injeksi contsructor, saya tidak perlu melakukan ejekan yang tidak perlu
kenobiwan
25

Masalah rasa. Itu keputusanmu.

Tapi saya bisa menjelaskan, mengapa saya tidak pernah menggunakan injeksi konstruktor .

  1. Saya tidak ingin menerapkan konstruktor untuk semua my @Service, @Repositoryand @Controllerbeans. Maksud saya, ada sekitar 40-50 biji kopi atau lebih. Setiap kali jika saya menambahkan bidang baru, saya harus memperluas konstruktor. Tidak, aku tidak menginginkannya dan aku tidak perlu.

  2. Bagaimana jika Bean Anda (Layanan atau Pengontrol) membutuhkan banyak biji lain untuk disuntikkan? Sebuah konstruktor dengan 4+ parameter sangat jelek.

  3. Jika saya menggunakan CDI, konstruktor bukan urusan saya.


EDIT # 1 : Vojtech Ruzicka mengatakan:

class memiliki terlalu banyak dependensi dan mungkin melanggar prinsip tanggung jawab tunggal dan harus difaktorkan ulang

Iya. Teori dan realitas. Berikut adalah contoh en: DashboardControllerdipetakan ke jalur tunggal *:8080/dashboard.

Saya DashboardControllermengumpulkan banyak informasi dari layanan lain untuk ditampilkan di halaman ikhtisar dasbor / sistem. Saya butuh pengontrol tunggal ini. Jadi saya harus mengamankan hanya satu jalur ini (auth dasar atau filter peran pengguna).

EDIT # 2 : Karena semua orang fokus pada 8 parameter dalam konstruktor ... Ini adalah contoh dunia nyata - kode warisan pelanggan. Saya telah mengubah itu. Argumentasi yang sama berlaku untuk saya untuk 4+ parameter.

Ini semua tentang injeksi kode, bukan konstruksi instance.

pelaku diet
sumber
34
Konstruktor yang sangat jelek dengan 8 dependensi sebenarnya luar biasa karena ini adalah tanda bahaya bahwa ada sesuatu yang salah, kelas memiliki terlalu banyak dependensi dan mungkin melanggar prinsip tanggung jawab tunggal dan harus diperbaiki. Ini sebenarnya hal yang bagus.
Vojtech Ruzicka
6
@VojtechRuzicka itu tidak bagus pasti tapi terkadang Anda tidak bisa menghindarinya.
pelaku diet
4
Saya akan mengatakan aturan praktis 3, apalagi 40-50, dependensi untuk kelas apa pun harus menjadi tanda bahwa Anda perlu melakukan refactor. Tidak mungkin kelas dengan 40 dependensi berpegang pada kepala sekolah tanggung jawab tunggal atau kepala sekolah buka / tutup.
Amin J
4
@AminJ Aturannya bagus, tapi kenyataannya berbeda. Perusahaan tempat saya bekerja sudah lebih dari 20 tahun dan kami memiliki banyak kode warisan. Refactoring adalah ide yang bagus, tetapi membutuhkan uang. Juga saya tidak tahu mengapa mengatakannya tetapi saya tidak bermaksud 40-50 ketergantungan, maksud saya 40-50 kacang, komponen, modul ...
dieter
7
@dit, situasi Anda jelas merupakan situasi di mana hutang teknis menyebabkan Anda membuat pilihan yang kurang optimal. Dengan kata-kata Anda sendiri, Anda berada dalam situasi di mana pengambilan keputusan Anda dipengaruhi secara signifikan oleh kode warisan yang berusia lebih dari 20 tahun. Saat memulai proyek baru, apakah Anda masih merekomendasikan injeksi lapangan, daripada injeksi konstruktor? Mungkin Anda harus memberikan peringatan dalam jawaban Anda untuk menunjukkan dalam kasus apa Anda akan memilih injeksi lapangan.
Umar Farooq Khawaja
0

Satu komentar lagi - Vojtech Ruzicka menyatakan bahwa Spring menyuntikkan kacang dalam tiga cara (jawaban dengan jumlah poin terbesar):

  1. Melalui konstruktor
  2. Melalui penyetel atau metode lain
  3. Melalui refleksi, langsung ke lapangan

Jawaban ini SALAH - karena UNTUK SETIAP JENIS INJEKSI SPRING MENGGUNAKAN REFLEKSI! Gunakan IDE, setel breakpoint pada penyetel / konstruktor, dan periksa.

Ini bisa jadi soal selera tapi bisa juga soal KASUS. @dieter memberikan kasus yang sangat baik saat injeksi lapangan lebih baik. Jika Anda menggunakan injeksi bidang dalam pengujian integrasi yang menyiapkan konteks Spring - argumen dengan kemampuan untuk diuji kelas juga tidak valid - kecuali Anda ingin menulis nanti pada pengujian untuk pengujian integrasi Anda;)

wujek.oczko
sumber