Saya curiga saya membuat kesalahan anak sekolah di sini, dan saya sedang mencari klarifikasi. Banyak kelas dalam solusi saya (C #) - berani saya katakan mayoritas - Saya akhirnya menulis antarmuka yang sesuai untuk. Misalnya antarmuka "ICalculator" dan kelas "Kalkulator" yang mengimplementasikannya, meskipun saya tidak akan pernah mungkin mengganti kalkulator itu dengan implementasi yang berbeda. Juga, sebagian besar kelas-kelas ini berada dalam proyek yang sama dengan dependensi mereka - mereka benar-benar hanya perlu internal
, tetapi akhirnya menjadi public
efek samping dari implementasi antarmuka masing-masing.
Saya pikir praktik menciptakan antarmuka untuk semuanya ini berakar dari beberapa kepalsuan: -
1) Saya awalnya berpikir bahwa sebuah antarmuka diperlukan untuk membuat unit test mock (saya menggunakan Moq), tetapi sejak itu saya menemukan bahwa sebuah kelas dapat diejek jika anggotanya adalah virtual
, dan ia memiliki konstruktor tanpa parameter (koreksi saya jika Aku salah).
2) Saya awalnya berpikir antarmuka diperlukan untuk mendaftarkan kelas dengan kerangka kerja IoC (Castle Windsor), misalnya
Container.Register(Component.For<ICalculator>().ImplementedBy<Calculator>()...
padahal sebenarnya saya bisa mendaftarkan tipe beton itu sendiri:
Container.Register(Component.For<Calculator>().ImplementedBy<Calculator>()...
3) Menggunakan antarmuka, misalnya parameter konstruktor untuk injeksi dependensi, menghasilkan "kopling longgar".
Jadi, apakah saya sudah gila dengan antarmuka ?! Saya mengetahui skenario di mana Anda akan "biasanya" menggunakan antarmuka, misalnya mengekspos API publik, atau untuk hal-hal seperti fungsionalitas "pluggable". Solusi saya memiliki sejumlah kecil kelas yang sesuai dengan kasus penggunaan tersebut, tetapi saya ingin tahu apakah semua antarmuka lain tidak perlu, dan harus dihapus? Mengenai poin 3) di atas, tidakkah saya akan melanggar "kopling longgar" jika saya melakukan ini?
Sunting : - Saya hanya bermain-main dengan Moq, dan tampaknya memerlukan metode untuk publik dan virtual, dan memiliki konstruktor tanpa parameter publik , agar dapat mengejek mereka. Jadi sepertinya saya tidak bisa memiliki kelas internal?
sumber
Jawaban:
Secara umum, jika Anda membuat antarmuka yang hanya memiliki satu implementor, Anda hanya menulis dua kali dan membuang-buang waktu. Antarmuka saja tidak menyediakan kopling longgar jika dipasangkan dengan erat ke satu implementasi ... Yang mengatakan, jika Anda ingin mengejek hal-hal ini dalam unit test, itu biasanya pertanda bagus bahwa Anda pasti akan membutuhkan lebih dari satu implementor secara nyata kode.
Saya akan menganggapnya sedikit bau kode jika hampir semua kelas Anda memiliki antarmuka sekalipun. Itu berarti mereka hampir semua bekerja dengan satu sama lain dalam satu atau lain cara. Saya menduga bahwa kelas melakukan terlalu banyak (karena tidak ada kelas pembantu) atau Anda sudah terlalu banyak abstrak (oh, saya ingin antarmuka penyedia gravitasi karena itu mungkin berubah!).
sumber
1) Sekalipun kelas konkret dapat diejek, masih mengharuskan Anda membuat anggota
virtual
dan menyediakan konstruktor tanpa parameter, yang mungkin mengganggu dan tidak diinginkan. Anda (atau anggota tim baru) akan segera menemukan diri Anda secara sistematis menambahkanvirtual
konstruktor dan parameterless di setiap kelas baru tanpa berpikir dua kali, hanya karena "begitulah cara kerjanya".Antarmuka adalah IMO ide yang jauh lebih baik ketika Anda perlu mengejek ketergantungan, karena memungkinkan Anda untuk menunda implementasi sampai ketika Anda benar-benar membutuhkannya, dan membuat kontrak ketergantungan yang jelas dan bagus.
3) Bagaimana itu kepalsuan?
Saya percaya antarmuka sangat bagus untuk mendefinisikan protokol pengiriman pesan antara objek yang berkolaborasi. Mungkin ada kasus ketika kebutuhan mereka masih bisa diperdebatkan, seperti halnya entitas domain misalnya. Namun, di mana pun Anda memiliki mitra yang stabil (yaitu, ketergantungan yang disuntikkan sebagai lawan dari referensi sementara) yang Anda butuhkan untuk berkomunikasi, Anda biasanya harus mempertimbangkan untuk menggunakan antarmuka.
sumber
Gagasan IoC adalah membuat jenis beton dapat diganti. Jadi bahkan jika (saat ini) Anda hanya memiliki satu Kalkulator yang mengimplementasikan ICalculator, implementasi kedua hanya dapat bergantung pada antarmuka dan bukan pada detail implementasi dari Kalkulator.
Oleh karena itu, versi pertama pendaftaran IoC-Container Anda adalah yang benar, dan yang harus Anda gunakan di masa depan.
Jika Anda membuat kelas Anda publik atau internal tidak benar-benar terkait, dan begitu juga dengan moq-ing.
sumber
Saya benar-benar akan lebih khawatir tentang fakta bahwa Anda tampaknya merasa perlu untuk "mengejek" hampir semua jenis di seluruh proyek Anda.
Tes ganda sebagian besar berguna untuk mengisolasi kode Anda dari dunia eksternal (lib pihak ketiga, disk IO, IO jaringan, dll.) Atau dari perhitungan yang sangat mahal. Menjadi terlalu liberal dengan kerangka isolasi Anda (apakah Anda BENAR-BENAR membutuhkannya? Apakah proyek Anda serumit itu?) Dapat dengan mudah mengarah pada tes yang digabungkan dengan implementasi, dan itu membuat desain Anda lebih kaku. Jika Anda menulis banyak tes yang memverifikasi bahwa beberapa kelas disebut metode X dan Y dalam urutan itu, dan kemudian meneruskan parameter a, b dan c ke metode Z, apakah Anda BENAR-BENAR mendapatkan nilai? Kadang-kadang Anda mendapatkan tes seperti ini saat menguji penebangan atau interaksi dengan lib pihak ketiga, tetapi itu harus menjadi pengecualian, bukan aturannya.
Segera setelah kerangka kerja isolasi Anda memberi tahu Anda bahwa Anda harus membuat barang publik dan bahwa Anda harus selalu memiliki konstruktor tanpa parameter, sekarang saatnya untuk menghindari, karena pada titik ini kerangka Anda memaksa Anda untuk menulis kode yang buruk (lebih buruk).
Berbicara lebih khusus tentang antarmuka, saya menemukan mereka berguna dalam beberapa kasus utama:
Ketika Anda perlu mengirim panggilan metode ke berbagai jenis, misalnya ketika Anda memiliki beberapa implementasi (ini adalah yang jelas, karena definisi antarmuka dalam kebanyakan bahasa hanya kumpulan metode virtual)
Ketika Anda harus dapat mengisolasi sisa kode Anda dari beberapa kelas. Ini adalah cara normal untuk mengabstraksi sistem file dan akses jaringan serta basis data dan sebagainya dari logika inti aplikasi Anda.
Ketika Anda membutuhkan inversi kontrol untuk melindungi batas aplikasi. Ini adalah salah satu cara paling ampuh untuk mengelola dependensi. Kita semua tahu modul tingkat tinggi dan modul tingkat rendah tidak boleh tahu tentang satu sama lain, karena mereka akan berubah karena alasan yang berbeda dan Anda TIDAK ingin memiliki ketergantungan pada HttpContext dalam model domain Anda atau pada beberapa kerangka kerja rendering Pdf di perpustakaan perkalian matriks Anda . Membuat kelas batas Anda mengimplementasikan antarmuka (bahkan jika mereka tidak pernah diganti) membantu melonggarkan kopling antar lapisan, dan secara drastis mengurangi risiko ketergantungan merembes melalui lapisan dari jenis yang mengetahui terlalu banyak jenis lainnya.
sumber
Saya condong ke arah gagasan bahwa, jika beberapa logika bersifat pribadi, maka implementasi logika itu seharusnya tidak menjadi perhatian bagi siapa pun, dan karena itu tidak boleh diuji. Jika beberapa logika cukup kompleks sehingga Anda ingin mengujinya, logika itu mungkin memiliki peran yang berbeda dan harus diketahui publik. Misalnya jika saya mengekstrak beberapa kode dalam metode pembantu pribadi, saya menemukan bahwa biasanya itu sesuatu yang bisa juga duduk di kelas publik yang terpisah (formatter, parser, mapper, apa pun).
Adapun antarmuka, saya membiarkan perlunya harus rintisan atau mengejek kelas mendorong saya. Hal-hal seperti repo, saya biasanya ingin dapat rintisan atau mengejek. Seorang mapper dalam tes integrasi, (biasanya) tidak begitu banyak.
sumber