Selama bertahun-tahun menggunakan C # /. NET untuk banyak proyek internal, kami memiliki satu perpustakaan yang tumbuh secara organik menjadi satu kumpulan besar barang. Itu disebut "Util", dan saya yakin banyak dari Anda telah melihat salah satu dari binatang buas ini dalam karier Anda.
Banyak bagian dari perpustakaan ini sangat mandiri, dan dapat dipecah menjadi proyek-proyek terpisah (yang kami ingin open-source). Tetapi ada satu masalah besar yang perlu dipecahkan sebelum ini dapat dirilis sebagai perpustakaan terpisah. Pada dasarnya, ada banyak dan banyak kasus dari apa yang saya sebut "dependensi opsional" antara perpustakaan ini.
Untuk menjelaskan hal ini dengan lebih baik, pertimbangkan beberapa modul yang merupakan kandidat yang baik untuk menjadi perpustakaan yang berdiri sendiri. CommandLineParser
adalah untuk parsing baris perintah. XmlClassify
adalah untuk serialisasi kelas ke XML. PostBuildCheck
melakukan pemeriksaan pada rakitan yang dikompilasi dan melaporkan kesalahan kompilasi jika gagal. ConsoleColoredString
adalah perpustakaan untuk literal string berwarna. Lingo
adalah untuk menerjemahkan antarmuka pengguna.
Masing-masing perpustakaan dapat digunakan sepenuhnya berdiri sendiri, tetapi jika mereka digunakan bersama-sama maka ada fitur tambahan yang berguna yang bisa didapat. Misalnya, baik CommandLineParser
dan XmlClassify
mengekspos fungsionalitas pemeriksaan pasca-pembangunan, yang mengharuskan PostBuildCheck
. Demikian pula, CommandLineParser
memungkinkan opsi dokumentasi disediakan menggunakan string string berwarna, yang membutuhkan ConsoleColoredString
, dan mendukung dokumentasi yang dapat diterjemahkan melalui Lingo
.
Jadi perbedaan utamanya adalah ini adalah fitur opsional . Seseorang dapat menggunakan parser baris perintah dengan string polos, tidak berwarna, tanpa menerjemahkan dokumentasi atau melakukan pemeriksaan post-build. Atau seseorang dapat membuat dokumentasi itu dapat diterjemahkan tetapi masih belum berwarna. Atau keduanya berwarna dan dapat diterjemahkan. Dll
Melihat melalui pustaka "Util" ini, saya melihat bahwa hampir semua pustaka yang berpotensi dipisah memiliki fitur opsional yang mengikatnya dengan pustaka lain. Jika saya benar-benar membutuhkan pustaka tersebut sebagai dependensi, maka kumpulan hal ini tidak benar-benar tidak kusut sama sekali: Anda pada dasarnya masih memerlukan semua pustaka jika Anda ingin menggunakan satu saja.
Apakah ada pendekatan yang ditetapkan untuk mengelola dependensi opsional seperti itu di .NET?
sumber
Jawaban:
Reaktor Perlahan.
Harapkan ini membutuhkan waktu untuk diselesaikan , dan mungkin terjadi beberapa kali pengulangan sebelum Anda dapat menghapus unit Utils sepenuhnya .
Pendekatan Keseluruhan:
Pertama-tama luangkan waktu dan pikirkan bagaimana Anda ingin rakitan utilitas ini terlihat ketika Anda selesai. Jangan terlalu khawatir tentang kode yang ada, pikirkan tujuan akhir. Misalnya, Anda mungkin ingin memiliki:
Buat proyek kosong untuk masing-masing proyek ini, dan buat referensi proyek yang sesuai (UI referensi Core, UI.WinForms referensi UI), dll.
Pindahkan salah satu dari buah yang menggantung rendah (kelas atau metode yang tidak menderita masalah ketergantungan) dari perakitan Utils Anda ke majelis target baru.
Dapatkan salinan Refactoring NDepend dan Martin Fowler untuk mulai menganalisis unit Utils Anda untuk mulai bekerja pada yang lebih sulit. Dua teknik yang akan membantu:
Menangani Antarmuka Opsional
Entah majelis referensi majelis lain, atau tidak. Satu-satunya cara lain untuk menggunakan fungsionalitas dalam perakitan non-terhubung adalah melalui antarmuka yang dimuat melalui refleksi dari kelas umum. Kelemahan dari hal ini adalah bahwa perakitan inti Anda harus mengandung antarmuka untuk semua fitur yang dibagikan, tetapi sisi baiknya adalah Anda dapat menggunakan utilitas sesuai kebutuhan tanpa "gumpalan" file DLL tergantung pada setiap skenario penempatan. Inilah cara saya menangani kasus ini, menggunakan string berwarna sebagai contoh:
Pertama, tentukan antarmuka umum dalam perakitan inti Anda:
Misalnya,
IStringColorer
antarmuka akan terlihat seperti:Kemudian, implementasikan antarmuka dalam perakitan dengan fitur. Sebagai contoh,
StringColorer
kelas akan terlihat seperti:Buat kelas
PluginFinder
(atau mungkin InterfaceFinder adalah nama yang lebih baik dalam kasus ini) yang dapat menemukan antarmuka dari file DLL di folder saat ini. Ini adalah contoh sederhana. Per @ EdWoodcock saran (dan saya setuju), ketika proyek Anda tumbuh saya akan menyarankan menggunakan salah satu kerangka kerja Ketergantungan Injeksi yang tersedia ( Common Serivce Locator dengan Unity and Spring.NET datang ke pikiran) untuk implementasi yang lebih kuat dengan lebih maju "temukan saya fitur itu "kemampuan, atau dikenal sebagai Pola Pencari Layanan . Anda dapat memodifikasinya sesuai dengan kebutuhan Anda.Terakhir, gunakan antarmuka ini di majelis Anda yang lain dengan memanggil metode FindInterface. Ini adalah contoh dari
CommandLineParser
:}
PALING PENTING: Tes, uji, uji antara setiap perubahan.
sumber
Anda dapat menggunakan antarmuka yang dideklarasikan di perpustakaan tambahan.
Cobalah untuk menyelesaikan kontrak (kelas via antarmuka) menggunakan injeksi dependensi (MEF, Unity dll). Jika tidak ditemukan, atur untuk mengembalikan turunan nol.
Kemudian periksa apakah instance adalah null, dalam hal ini Anda tidak melakukan fungsionalitas tambahan.
Ini sangat mudah dilakukan dengan MEF, karena ini adalah buku teks yang digunakan untuk itu.
Ini akan memungkinkan Anda untuk mengkompilasi perpustakaan, dengan biaya membaginya menjadi n + 1 dll.
HTH.
sumber
Saya pikir saya akan memposting opsi yang paling layak yang telah kami buat sejauh ini, untuk melihat apa yang dipikirkan.
Pada dasarnya, kami akan memisahkan setiap komponen menjadi pustaka dengan nol referensi; semua kode yang membutuhkan referensi akan ditempatkan ke dalam
#if/#endif
blok dengan nama yang sesuai. Misalnya, kodeCommandLineParser
yang menanganiConsoleColoredString
s akan ditempatkan#if HAS_CONSOLE_COLORED_STRING
.Setiap solusi yang ingin memasukkan hanya
CommandLineParser
dapat dengan mudah melakukannya, karena tidak ada ketergantungan lebih lanjut. Namun, jika solusinya juga mencakupConsoleColoredString
proyek, programmer sekarang memiliki opsi untuk:CommandLineParser
keConsoleColoredString
HAS_CONSOLE_COLORED_STRING
define keCommandLineParser
file proyek.Ini akan membuat fungsionalitas yang relevan tersedia.
Ada beberapa masalah dengan ini:
Agak tidak cantik, tapi tetap saja, ini yang paling dekat dengan kami.
Satu gagasan lebih lanjut yang kami pertimbangkan adalah menggunakan konfigurasi proyek daripada mengharuskan pengguna untuk mengedit file proyek perpustakaan. Tapi ini benar-benar tidak bisa dijalankan di VS2010 karena ia menambahkan semua konfigurasi proyek ke solusi yang tidak diinginkan .
sumber
Saya akan merekomendasikan buku Pengembangan Aplikasi Brownfield di .Net . Dua bab yang relevan secara langsung adalah 8 & 9. Bab 8 berbicara tentang menyampaikan aplikasi Anda, sementara bab 9 berbicara tentang menjinakkan dependensi, inversi kontrol, dan dampaknya pada pengujian.
sumber
Pengungkapan penuh, saya seorang pria Jawa. Jadi saya mengerti Anda mungkin tidak mencari teknologi yang akan saya sebutkan di sini. Tapi masalahnya sama, jadi mungkin itu akan mengarahkan Anda ke arah yang benar.
Di Jawa, ada sejumlah sistem build yang mendukung gagasan repositori artefak terpusat yang menampung "artefak" yang dibangun - sepengetahuan saya ini agak analog dengan GAC di .NET (mohon eksekusi ketidaktahuan saya jika ini adalah anaologi yang tegang) tetapi lebih dari itu karena digunakan untuk menghasilkan bangunan berulang yang independen pada setiap titik waktu.
Bagaimanapun, fitur lain yang didukung (dalam Maven, misalnya) adalah ide ketergantungan OPTIONAL, kemudian tergantung pada versi atau rentang tertentu dan berpotensi mengecualikan dependensi transitif. Ini kedengarannya seperti apa yang Anda cari, tapi saya bisa saja salah. Lihatlah halaman intro ini tentang manajemen ketergantungan dari Maven dengan teman yang mengenal Java dan lihat apakah masalahnya terdengar familier. Ini akan memungkinkan Anda untuk membangun aplikasi Anda dan membangunnya dengan atau tanpa ketersediaan dependensi ini.
Ada juga konstruksi jika Anda benar-benar dinamis, arsitektur yang dapat dicolokkan; salah satu teknologi yang mencoba menangani bentuk resolusi ketergantungan runtime ini adalah OSGI. Ini adalah mesin di balik sistem plugin Eclipse . Anda akan melihatnya dapat mendukung dependensi opsional dan rentang versi minimum / maksimum. Tingkat modularitas runtime ini membebankan sejumlah kendala pada Anda dan bagaimana Anda berkembang. Kebanyakan orang bisa bertahan dengan tingkat modularitas yang disediakan oleh Maven.
Satu kemungkinan gagasan lain yang bisa Anda perhatikan yang mungkin merupakan urutan besarnya yang lebih sederhana untuk diterapkan bagi Anda adalah dengan menggunakan gaya arsitektur Pipes and Filters. Ini adalah apa yang membuat UNIX ekosistem yang sukses dan bertahan lama yang telah bertahan dan berkembang selama setengah abad. Lihatlah artikel ini tentang Pipa dan Filter di .NET untuk beberapa ide tentang bagaimana menerapkan pola semacam ini dalam kerangka kerja Anda.
sumber
Mungkin buku "Desain perangkat lunak C ++ skala besar" oleh John Lakos berguna (tentu saja C # dan C ++ atau tidak sama, tetapi Anda dapat menyaring teknik yang berguna dari buku).
Pada dasarnya, faktor ulang dan pindahkan fungsionalitas yang menggunakan dua atau lebih pustaka ke dalam komponen terpisah yang bergantung pada pustaka ini. Jika perlu, gunakan teknik seperti jenis buram dll.
sumber