Lebih baik menulis perpustakaan .NET Anda dengan batasan COM, atau memisahkan perpustakaan .NET Anda dari Interop?

9

Saya menemukan artikel yang menarik ini: Bagaimana Saya Datang untuk Mencintai Interoperabilitas COM pada CodeProject, yang membuat saya berpikir ...

Penulis berpendapat bahwa mereka tidak ingin ada COM-ities di perpustakaan NET mereka. Karena mengambil dari keindahan perpustakaan NET mereka. Sebagai gantinya, mereka lebih suka menulis pustaka Interop terpisah yang memperlihatkan pustaka .NET mereka ke COM. Pustaka Interop ini akan menangani fakta bahwa COM tidak mendukung Konstruktor dengan Parameter, Metode Kelebihan Beban, Generik, Warisan, Metode Statis, dll.

Dan sementara saya pikir itu sangat baru, bukankah itu hanya sesuai dengan proyek?

  • Sekarang Anda perlu menguji unit perpustakaan .NET DAN perpustakaan Interop Anda.
  • Sekarang Anda perlu menghabiskan waktu mencari tahu cara bekerja di sekitar pustaka .NET yang indah dan paparkan ke COM.
  • Anda perlu, dua kali lipat atau tiga kali lipat jumlah kelas Anda secara efektif.

Saya pasti bisa mengerti jika Anda membutuhkan perpustakaan Anda untuk mendukung COM dan non-COM. Namun, jika Anda hanya ingin menggunakan COM apakah desain semacam ini membawa manfaat yang tidak saya lihat? Apakah Anda hanya mendapatkan manfaat dari bahasa C #?

Atau, apakah itu membuat versi perpustakaan Anda lebih mudah dengan memberikan Anda sebuah pembungkus? Apakah itu membuat Tes Unit Anda berjalan lebih cepat dengan tidak memerlukan penggunaan COM?

robodude666
sumber
2
Maksud Anda, selain bisa menggunakan alat yang berguna untuk membuat kode nyata lebih baik / bersih? Dan menyediakan jalur migrasi yang jelas untuk hal-hal COM (yang mungkin merupakan warisan) itu?
Telastyn
3
Ini seperti pola fasad yang ditulis besar di seluruh namespace. Saya pikir itu pintar untuk mengaturnya seperti ini jika Anda ingin menggunakan fitur. NET tetapi benar-benar membutuhkan OLE Automation. Pembungkus COM Anda pada dasarnya adalah untuk mengubah idiom modern (tidak dapat diubah? Generik?) Menjadi objek COM yang lebih tua dengan konstruktor tanpa parameter, objek yang sangat bisa berubah, dan SAFEARRAY untuk daftar generik Anda. Jika Anda mendukung komponen .NET lainnya atau benar-benar jatuh cinta dengan gaya baru ini, ini masuk akal. Jika Anda HANYA mendukung COM, mengapa tidak menulis semuanya dalam VB6 (32 bit) dan hanya menyimpan satu idiom?
Mike
3
@MasonWheeler: Sudah usang dengan cara yang sama semua perangkat lunak yang lebih dari 6 bulan adalah "warisan", apakah saya benar?
whatsisname
2
@MasonWheeler COM tidak dapat didepresiasi tidak peduli berapa banyak orang (termasuk beberapa grup dalam Microsoft) yang menginginkannya karena ini masih merupakan pilihan terbaik untuk mengendalikan berbagai hal di luar proses.
Mike
2
@ Mike, saya benar-benar melihat porting proyek VB6 ke C # .NET. API yang menghadap pengguna yang kami gunakan sangat sederhana, namun kode di belakang layar tidak dapat dipertahankan apa adanya. Saya berharap dapat mengimplementasikan mayoritas pustaka di C # .NET dan menyediakan Interop Facade yang menyediakan API sederhana yang berinteraksi dengan berbagai kelas.
robodude666

Jawaban:

7

Namun, jika Anda hanya ingin menggunakan COM apakah desain semacam ini membawa manfaat yang tidak saya lihat?

Potongan pertanyaan Anda ini adalah bagian terpenting dari keputusan desain Anda di sini, dan jawabannya adalah (seperti biasa) tergantung .

Jika Anda hanya ingin menggunakan pustaka melalui COM Interop, maka Anda harus merancang seluruh sistem dengan mempertimbangkan hal ini. Tidak ada alasan untuk melipatgandakan jumlah baris. Semakin banyak LoC dalam sistem, semakin banyak cacat yang dimilikinya. Karena itu Anda harus merancang sistem Anda untuk hanya menggunakan fungsionalitas yang kompatibel dengan COM.

Namun , saya tidak bisa memikirkan alasan bagus untuk membatasi penggunaan perpustakaan .Net ke COM. Mengapa Anda tidak ingin menggunakan perakitan dalam kode .Net lainnya? Tidak masuk akal membatasi diri dengan cara ini.

Saya memiliki beberapa pengalaman dengan ini, jadi saya akan membagikan bagaimana saya menanganinya. Tentu tidak ideal. Saya telah membuat beberapa trade off. Saya hanya menggunakan fasad untuk kelas COM (antarmuka benar-benar) yang tidak kompatibel dengan idiom .Net yang lebih baru, jika tidak, saya langsung mengekspos antarmuka .Net ke COM. Ini pada dasarnya berarti bahwa saya menggunakan fasad untuk semua antarmuka yang menggunakan generik. Generik adalah satu-satunya hal yang saya temui yang hanya tidak kompatibel. Sayangnya, ini berarti fasad untuk apa pun yang mengembalikan IEnumerable<T>, yang cukup umum.

Namun, ini tidak seburuk yang kelihatannya, karena kelas fasad Anda mewarisi dari kelas .Net Anda dan mengimplementasikan antarmuka Interop tertentu. Sesuatu seperti ini.

namespace Company.Product.Feature
{
    public class MyClass : INetInterface 
    {
        // lots of code
    }
}

namespace Company.Product.Interop
{
    public class MyClass : Feature.MyClass, IComInterface
    {
        // over ride only the  incompatible properties/methods
    }
}

Ini menjaga kode duplikat Anda ke minimum absolut dan melindungi antarmuka COM Anda dari perubahan "tidak disengaja". Saat kelas inti / antarmuka Anda berubah, kelas adaptor Anda harus menggunakan lebih banyak metode (atau menghancurkan antarmuka dan menabrak nomor versi utama). Di sisi lain, tidak semua kode Interop Anda disimpan di Interopnamespace, yang dapat menyebabkan organisasi file membingungkan. Seperti yang saya katakan, ini merupakan trade off.

Untuk contoh lengkap ini, Anda dapat menelusuri folder SourceControl dan Interop dari repo saya. ISourceControlProviderdan GitProviderakan menjadi minat khusus.

Bebek karet
sumber
Terima kasih atas tanggapannya @RubberDuck! Saya berlari melintasi proyek RubberDuck beberapa hari yang lalu; sudah sangat menarik membaca kode. Apakah Anda bisa lolos IEnumerableke VBA? Juga, di Rubberduck.Interop.GitProvidermengapa Anda mendefinisikan konstruktor berparameter jika COM tidak mendukungnya?
robodude666
Ya, Anda dapat melewati IEnumerableCOM, tetapi harus versi yang lebih lama, non-generik. Adapun konstruktor, lihat SourceControlClassFactory. Pada dasarnya, Anda memalsukannya dengan menginisialisasi instance kelas yang tidak memerlukan parameter untuk ctornya dan menggunakannya untuk mendapatkan akses ke instance baru dari kelas API Anda.
RubberDuck
Saya menduga bahwa apa pun yang didefinisikan dalam ComVisible(true)kelas / antarmuka Anda yang tidak didukung COM hanya "diabaikan"? Saya juga menduga jika Anda memiliki kelas dengan nama yang sama didefinisikan dalam beberapa ruang nama Anda harus memberikan yang unik ProgIdjika Anda ingin mengekspos lebih dari satu dari mereka?
robodude666
Iya. Itu benar dan staticmetode diabaikan. Saya mencoba untuk melihat apakah ada yang setara dengan VB6 yang VB_PredeclaredIdtersedia. Saya berpikir mungkin membuat instance default.
RubberDuck
7

itu menghilangkan keindahan perpustakaan .NET mereka

Penulis itu membutuhkan tamparan bangun di wajah. Untuk setiap proyek profesional, tujuannya adalah membuat perangkat lunak yang berfungsi yang ingin digunakan orang. Cita-cita sesat tentang kecantikan datang lama setelah itu. Jika itu satu-satunya alasan mereka, penulis telah kehilangan semua kredibilitas.

Mampu menandai kelas Anda sebagai yang terlihat dan dapat berbicara dengan kode .NET Anda melalui COM sangat berguna. Membuang manfaat besar bagi produktivitas untuk kecantikan itu gila.

Namun, membuat hal-hal berfungsi dengan COM memang memerlukan beberapa trade-off, membutuhkan konstruktor no-arg adalah salah satunya, dan beberapa hal lain yang Anda sebutkan. Jika itu adalah fitur yang tidak Anda gunakan, atau tidak ada salahnya Anda tidak menggunakannya, maka ada banyak pekerjaan yang harus disimpan dengan menggunakan kelas yang sama untuk COM dan non com.

Di sisi lain, jika klien COM Anda mengharapkan antarmuka yang berbeda secara dramatis, memiliki ketergantungan atau batasan lain yang tidak menyenangkan untuk ditangani dalam kelas .NET Anda, atau Anda berharap untuk secara signifikan mengubah kelas .NET Anda dan perlu mempertahankan COM mundur- kompatibilitas, maka tentu saja membuat kelas Interop untuk menjembatani kesenjangan itu.

Tapi jangan berpikir tentang "kecantikan". Mampu mengekspos COM melalui .NET dimaksudkan untuk menghemat pekerjaan Anda. Jangan membuat lebih banyak pekerjaan untuk diri sendiri.

Apa namanya
sumber
+1. Sementara saya suka keindahan tombol hitam pada kepraktisan kotak hitam secara signifikan lebih penting.
gbjbaanb
Sebenarnya, orang yang memperkenalkan istilah "keindahan" ke dalam diskusi ini mungkin perlu bangun-tampar, dan itu bukan penulis artikel lain itu.
Doc Brown
2
Hanya catatan tambahan, jika konstruktor Anda saat ini membutuhkan args, yang terbaik adalah menyediakan pabrik kelas untuk komponen COM Anda, daripada mendesain ulang antarmuka / API yang ada.
RubberDuck
1
Apakah mungkin mengekspos pabrik Anda sebagai kelas "GlobalMultiUse"? Jadi Anda bisa melakukannya Set oObject = MyCOMInterop.NewMyClass()tanpa harus membuat instantiate objek Factory baru?
robodude666
Itu pertanyaan yang bagus sekali @ robodude666. Sekarang Anda menyebutkannya, saya harap begitu.
RubberDuck
3

Yah, agar adil, penulis asli artikel itu tidak menggunakan kata "keindahan" di mana pun dalam artikelnya, yaitu Anda, yang mungkin memberi kesan bias pada mereka yang tidak meluangkan waktu untuk membaca artikel dengan diri.

Sejauh yang saya mengerti artikel itu, perpustakaan .NET sudah ada dan ditulis tanpa COM - mungkin karena pada saat perpustakaan dibuat, tidak ada persyaratan untuk mendukung COM.

Kemudian, persyaratan tambahan untuk mendukung COM diperkenalkan. Menghadapi situasi seperti itu, Anda memiliki dua opsi:

  • mendesain ulang lib asli untuk membuatnya kompatibel dengan COM interop (dengan risiko kerusakan)

  • biarkan pustaka kerja asli sebagian besar apa adanya, dan alih-alih buatlah rakitan terpisah dengan fungsi "wrapper" (atau "adaptor")

Membuat pembungkus atau adaptor adalah pola desain yang terkenal (atau dalam hal ini lebih merupakan pola arsitektur), dan pro dan kontra juga terkenal. Satu argumen untuk itu adalah "pemisahan keprihatinan" yang lebih baik, dan itu tidak ada hubungannya dengan kecantikan.

Untuk masalah Anda

Sekarang Anda perlu menguji unit perpustakaan .NET DAN perpustakaan Interop Anda.

Saat Anda memperkenalkan antarmuka COM ke pustaka .NET yang ada dengan mendesain ulang, Anda harus menguji antarmuka itu juga. Memperkenalkan adaptor yang ditulis secara manual mungkin memerlukan beberapa tes tambahan bahwa antarmuka berfungsi, tentu saja, tetapi tidak perlu menyalin semua tes unit fungsionalitas bisnis inti lib. Jangan lupa itu hanyalah antarmuka ke lib.

Sekarang Anda perlu menghabiskan waktu mencari tahu cara bekerja di sekitar pustaka .NET yang indah dan paparkan ke COM.

Ketika Anda harus mendesain ulang perpustakaan asli Anda harus mencari tahu juga, dan saya kira itu akan jauh lebih berhasil.

Anda perlu, dua kali lipat atau tiga kali lipat jumlah kelas Anda secara efektif.

Hanya untuk kelas-kelas yang diekspos melalui antarmuka COM publik, yang seharusnya menjadi bagian kelas kecil dari perpustakaan asli.

Mengatakan bahwa, untuk situasi yang berbeda yang Anda sebutkan, ketika Anda sudah tahu Anda harus mendukung COM sebagai warga negara kelas pertama sejak awal, saya pikir Anda benar: orang harus menghemat kerumitan menambahkan lib adaptor yang terpisah dan merancang Lib .NET dengan dukungan COM secara langsung.

Doc Brown
sumber
(+1) Namun, "... yang seharusnya merupakan sejumlah kecil ... perpustakaan asli" terkadang hanya benar. Ada banyak jenis proyek yang dirancang dalam "gaya perpustakaan kelas", di mana satu kelas sama dengan satu tujuan yang terlihat oleh publik, dan bahwa kelas menyusun untuk menimbulkan fungsionalitas yang menarik. Dalam hal ini, mungkin ada pemetaan satu-ke-satu antara jumlah kelas .NET dan jumlah kelas interop COM.
rwong