Saya telah merenungkan bagaimana menyeimbangkan desain yang dapat diuji menggunakan injeksi ketergantungan dengan menyediakan API publik sederhana yang tetap. Dilema saya adalah: orang ingin melakukan sesuatu seperti var server = new Server(){ ... }
dan tidak perlu khawatir menciptakan banyak dependensi dan grafik dependensi yang Server(,,,,,,)
mungkin dimiliki. Saat berkembang, saya tidak terlalu khawatir, karena saya menggunakan kerangka kerja IoC / DI untuk menangani semua itu (saya tidak menggunakan aspek manajemen siklus hidup dari wadah apa pun, yang akan memperumit masalah lebih lanjut).
Sekarang, dependensi tidak mungkin diimplementasikan kembali. Komponen dalam hal ini hampir murni untuk testabilitas (dan desain yang layak!) Daripada membuat jahitan untuk ekstensi, dll. Orang akan 99,999% dari waktu ingin menggunakan konfigurasi default. Begitu. Saya bisa membuat hardcode dependensi. Tidak ingin melakukan itu, kami kehilangan pengujian kami! Saya bisa menyediakan konstruktor default dengan dependensi hard-coded dan yang membutuhkan dependensi. Itu ... berantakan, dan mungkin membingungkan, tetapi layak. Saya bisa membuat dependensi menerima konstruktor internal dan membuat unit saya menguji teman perakitan (dengan asumsi C #), yang merapikan API publik tetapi meninggalkan perangkap tersembunyi yang jahat mengintai untuk pemeliharaan. Memiliki dua konstruktor yang secara implisit terhubung daripada secara eksplisit akan menjadi desain yang buruk secara umum dalam buku saya.
Saat ini tentang kejahatan paling sedikit yang bisa saya pikirkan. Pendapat? Kebijaksanaan?
HttpListener
konstruktor berubah makaServer
kelas juga harus berubah. Mereka digabungkan. Solusi yang lebih baik adalah apa yang @Winston Ewert katakan di bawah ini, yaitu menyediakan kelas default dengan dependensi yang ditentukan sebelumnya, di luar API perpustakaan. Maka programmer tidak dipaksa untuk mengatur semua dependensi karena Anda membuat keputusan untuknya, tetapi dia masih memiliki fleksibilitas untuk mengubahnya nanti. Ini adalah masalah besar ketika Anda memiliki dependensi pihak ketiga.Dalam Java dalam kasus seperti itu, biasanya memiliki konfigurasi "default" yang dibangun oleh pabrik, tetap independen dari server yang berperilaku "benar". Jadi, pengguna Anda menulis:
sementara
Server
konstruktor masih menerima semua dependensinya dan dapat digunakan untuk kustomisasi mendalam.sumber
Bagaimana dengan menyediakan subclass yang menyediakan "api publik"
Pengguna dapat
new StandardServer()
dan sedang dalam perjalanan. Mereka juga dapat menggunakan kelas dasar Server jika mereka ingin lebih mengontrol bagaimana fungsi server. Ini kurang lebih pendekatan yang saya gunakan. (Saya tidak menggunakan kerangka kerja karena saya belum melihat intinya.)Ini masih memperlihatkan api internal, tapi saya pikir Anda harus melakukannya. Anda telah membagi objek Anda menjadi berbagai komponen berguna yang harus bekerja secara independen. Tidak ada yang tahu kapan pihak ketiga mungkin ingin menggunakan salah satu komponen secara terpisah.
sumber
Itu tidak tampak seperti "perangkap tersembunyi yang jahat" bagi saya. Konstruktor publik seharusnya hanya memanggil yang internal dengan dependensi "default". Selama jelas bahwa konstruktor publik tidak boleh dimodifikasi, semuanya harus baik-baik saja.
sumber
Saya sangat setuju dengan pendapat Anda. Kami tidak ingin mencemari batas penggunaan komponen hanya untuk tujuan pengujian unit. Ini adalah seluruh masalah solusi pengujian berbasis DI.
Saya akan menyarankan untuk menggunakan InjectableFactory / InjectableInstance pola. InjectableInstance adalah kelas utilitas sederhana yang dapat digunakan kembali yang merupakan value holder yang bisa diubah . Antarmuka komponen berisi referensi ke pemegang nilai ini yang diinisialisasi dengan implementasi standar. Antarmuka akan memberikan metode singleton get () yang mendelegasikan kepada pemegang nilai. Klien komponen akan memanggil metode get () alih-alih yang baru untuk mendapatkan instance implementasi untuk menghindari ketergantungan langsung. Selama waktu pengujian, kami dapat secara opsional mengganti implementasi standar dengan tiruan. Setelah ini saya akan menggunakan satu contoh TimeProviderkelas yang merupakan abstraksi Date () sehingga memungkinkan pengujian unit untuk menyuntikkan dan mengejek untuk mensimulasikan momen yang berbeda. Maaf saya akan menggunakan java di sini karena saya lebih akrab dengan java. C # harus serupa.
InjectableInstance cukup mudah diimplementasikan, saya punya referensi implementasi di java. Untuk detailnya, silakan lihat posting blog saya Ketergantungan Indirection dengan Injectable Factory
sumber