Bagaimana saya bisa mengurangi upaya manual untuk membungkus perpustakaan pihak ketiga dengan model objek yang lebih besar?

16

Seperti penulis pertanyaan ini dari 2012 dan yang ini dari 2013 , saya memiliki perpustakaan pihak ke-3 yang harus saya bungkus untuk menguji aplikasi saya dengan benar. Jawaban teratas menyatakan:

Anda selalu ingin membungkus tipe dan metode pihak ketiga di belakang sebuah antarmuka. Ini bisa membosankan dan menyakitkan. Terkadang Anda dapat menulis pembuat kode atau menggunakan alat untuk melakukan ini.

Dalam kasus saya, perpustakaan adalah untuk model objek dan akibatnya memiliki sejumlah besar kelas dan metode yang perlu dibungkus agar strategi ini berhasil. Di luar sekadar "membosankan dan menyakitkan", ini menjadi penghalang sulit untuk pengujian.

Dalam 4 tahun sejak pertanyaan ini, saya sadar bahwa kerangka kerja isolasi telah berjalan jauh. Pertanyaan saya adalah: apakah sekarang ada cara yang lebih sederhana untuk mencapai efek pembungkus penuh perpustakaan pihak ke-3? Bagaimana saya bisa menghilangkan rasa sakit dari proses ini dan mengurangi upaya manual?


Pertanyaan saya bukanlah duplikat dari pertanyaan yang saya tautkan pada awalnya, karena pertanyaan saya adalah tentang mengurangi upaya pembungkus secara manual. Pertanyaan-pertanyaan lain hanya menanyakan apakah pembungkus itu masuk akal, bukan bagaimana upaya itu dapat dijaga tetap kecil.

Tom Wright
sumber
Bahasa pemrograman apa, dan perpustakaan macam apa yang Anda bicarakan?
Doc Brown
@DocBrown C # dan perpustakaan manipulasi PDF.
Tom Wright
2
Saya memulai posting di meta untuk menemukan beberapa dukungan untuk membuka kembali pertanyaan Anda.
Doc Brown
Terima kasih @DocBrown - Saya berharap akan ada beberapa perspektif menarik di luar sana.
Tom Wright
1
Ketika kami tidak mendapatkan jawaban yang lebih baik dalam 48 jam ke depan, saya akan memberi hadiah untuk ini.
Doc Brown

Jawaban:

4

Dengan anggapan Anda tidak mencari kerangka kerja yang mengejek, karena sangat ultra-ubiquitous dan mudah ditemukan , ada beberapa hal yang perlu diperhatikan di muka:

  1. Ada "tidak pernah" apa pun yang harus "selalu" Anda lakukan.
    Tidak selalu terbaik untuk membungkus perpustakaan pihak ketiga. Jika aplikasi Anda secara intrinsik tergantung pada perpustakaan, atau jika itu benar-benar dibangun di sekitar satu atau dua perpustakaan inti, jangan buang waktu Anda membungkusnya. Jika perpustakaan berubah, aplikasi Anda harus tetap berubah .
  2. Boleh saja menggunakan tes integrasi.
    Ini terutama berlaku di sekitar batas yang stabil, intrinsik untuk aplikasi Anda, atau tidak dapat dengan mudah diejek. Jika syarat-syarat itu terpenuhi, membungkus dan mengejek akan menjadi rumit dan membosankan. Dalam hal ini, saya akan menghindari keduanya: jangan membungkus dan tidak mengejek; tulis saja tes integrasi. (Jika pengujian otomatis adalah tujuan.)
  3. Alat dan kerangka kerja tidak dapat menghilangkan kompleksitas logis.
    Pada prinsipnya, alat hanya bisa mengurangi boilerplate. Tetapi, tidak ada algoritma yang dapat diotomatisasi untuk mengambil antarmuka yang kompleks dan membuatnya mudah - apalagi menggunakan antarmuka X dan mengadaptasinya sesuai dengan kebutuhan Anda. (Hanya Anda yang tahu bahwa algoritma!) Jadi, sementara ada diragukan lagi alat yang dapat menghasilkan pembungkus tipis, saya sarankan bahwa mereka belum di mana-mana karena, pada akhirnya, Anda masih perlu untuk kode hanya cerdas, dan karena itu secara manual, terhadap antarmuka bahkan jika itu tersembunyi di balik pembungkus.

Yang mengatakan, ada taktik yang dapat Anda gunakan dalam banyak bahasa untuk menghindari merujuk langsung ke kelas. Dan dalam beberapa kasus, Anda dapat "berpura-pura" antarmuka atau pembungkus tipis yang sebenarnya tidak ada. Dalam C #, misalnya, saya akan pergi salah satu dari dua rute:

  1. Gunakan pabrik dan pengetikan implisit .

Anda dapat menghindari upaya membungkus sepenuhnya kelas yang kompleks dengan kombo kecil ini:

// "factory"
class PdfDocumentFactory {
  public static ExternalPDFLibraryDocument Build() {
    return new ExternalPDFLibraryDocument();
  }
}

// code that uses the factory.
class CoreBusinessEntity {
  public void DoImportantThings() {
    var doc = PdfDocumentFactory.Build();

    // ... i have no idea what your lib does, so, I'm making stuff but.
    // but, you can do whatever you want here without explicitly
    // referring to the library's actual types.
    doc.addHeader("Wee");
    doc.getAllText().makeBiggerBy(4).makeBold().makeItalic();
    return doc.exportBinaryStreamOrSomething();
  }
}

Jika Anda dapat menghindari menyimpan objek-objek ini sebagai anggota, baik melalui pendekatan yang lebih "fungsional" atau dengan menyimpannya dalam kamus (atau apa pun ), pendekatan ini memiliki manfaat memeriksa tipe waktu kompilasi tanpa entitas bisnis inti Anda perlu tahu persis kelas apa yang mereka kerjakan.

Yang diperlukan hanyalah bahwa, pada waktu kompilasi, kelas yang dikembalikan oleh pabrik Anda sebenarnya memiliki metode yang digunakan objek bisnis Anda.

  1. Gunakan pengetikan dinamis .

Ini berada dalam nada yang sama dengan menggunakan pengetikan tersirat , tetapi melibatkan pengorbanan lain: Anda kehilangan pemeriksaan tipe kompilasi dan mendapatkan kemampuan untuk menambahkan dependensi eksternal secara anonim sebagai anggota kelas dan menyuntikkan dependensi Anda.

class CoreBusinessEntity {
  dynamic Doc;

  public void InjectDoc(dynamic Doc) {
    Doc = doc;
  }

  public void DoImortantThings() {
    Doc.addHeader("Wee");
    Doc.getAllText().makeBiggerBy(4).makeBold().makeItalic();
    return Doc.exportBinaryStreamOrSomething();
  }
}

Dengan kedua taktik ini, ketika tiba saatnya untuk mengejek ExternalPDFLibraryDocument, seperti yang saya katakan sebelumnya, Anda memiliki beberapa pekerjaan yang harus dilakukan - tetapi, itu adalah pekerjaan yang perlu Anda lakukan juga . Dan, dengan konstruksi ini, Anda telah menghindari mendefinisikan 100-an kelas bungkus kecil tipis. Anda cukup menggunakan perpustakaan tanpa melihatnya langsung - sebagian besar.

Dengan semua yang dikatakan, ada tiga alasan besar yang saya masih akan mempertimbangkan secara eksplisit membungkus perpustakaan pihak ketiga - tidak ada yang akan mengisyaratkan menggunakan alat atau kerangka kerja:

  1. Perpustakaan spesifik tidak intrinsik ke aplikasi.
  2. Akan sangat mahal untuk menukar tanpa membungkusnya.
  3. Saya tidak suka API itu sendiri.

Jika saya tidak memiliki masalah level di ketiga area tersebut, Anda tidak melakukan upaya signifikan untuk menyelesaikannya. Dan, jika Anda memiliki kekhawatiran di ketiga area, pembungkus tipis yang dibuat secara otomatis tidak akan membantu.

Jika Anda telah memutuskan untuk membungkus pustaka, penggunaan waktu Anda yang paling efisien dan efektif adalah membangun aplikasi Anda terhadap antarmuka yang Anda inginkan ; tidak menentang API yang ada.

Dengan kata lain, perhatikan nasihat klasik: Tunda setiap keputusan yang Anda bisa. Bangun "inti" aplikasi Anda terlebih dahulu. Kode terhadap antarmuka yang pada akhirnya akan melakukan apa yang Anda inginkan, yang pada akhirnya akan dipenuhi oleh "hal periferal" yang belum ada. Jembatan kesenjangan yang diperlukan.

Upaya ini mungkin tidak terasa seperti menghemat waktu; tetapi jika Anda merasa perlu pembungkus, ini adalah cara paling efisien untuk melakukannya dengan aman.

Pikirkan seperti ini.

Anda perlu kode terhadap pustaka ini di beberapa sudut gelap kode Anda - bahkan jika sudah selesai. Jika Anda mengejek perpustakaan selama pengujian, ada upaya manual yang tidak dapat dihindari di sana - bahkan jika sudah selesai. Tapi, itu tidak berarti Anda harus secara langsung mengakui perpustakaan itu dengan nama di sebagian besar aplikasi Anda.

TLDR

Jika perpustakaan layak untuk dibungkus, gunakan taktik untuk menghindari rujukan luas, langsung ke perpustakaan pihak ke-3 Anda, tetapi jangan mengambil jalan pintas untuk menghasilkan pembungkus tipis. Bangun logika bisnis Anda terlebih dahulu, pertimbangkan antarmuka Anda, dan jalankan adapter Anda secara organik, sesuai kebutuhan.

Dan, jika itu yang terjadi, jangan takut dengan tes integrasi. Mereka sedikit fuzzier, tetapi mereka masih menawarkan bukti kode kerja, dan mereka masih dapat dengan mudah dibuat untuk menjaga regresi.

svidgen
sumber
2
Ini adalah pos lain yang tidak menjawab pertanyaan - yang secara eksplisit bukan "kapan harus membungkus", tetapi "bagaimana mengurangi upaya manual".
Doc Brown
1
Maaf, tapi saya pikir Anda melewatkan inti pertanyaan. Saya tidak melihat bagaimana saran Anda dapat mengurangi upaya manual dalam pembungkus. Asumsikan lib yang dipertaruhkan memiliki model objek yang kompleks seperti API, dengan beberapa lusin kelas dan ratusan metode. API-nya baik-baik saja untuk penggunaan standar, tetapi bagaimana membungkusnya untuk pengujian unit dengan sedikit rasa sakit / usaha?
Doc Brown
1
TLDR; jika Anda menginginkan poin bonus, beri tahu kami sesuatu yang belum kami ketahui ;-)
Doc Brown
1
@DocBrown Saya tidak melewatkan intinya. Tapi, saya pikir Anda melewatkan inti jawaban saya. Menginvestasikan usaha yang tepat menghemat banyak pekerjaan. Ada implikasi pengujian - tetapi itu hanya efek samping. Menggunakan alat untuk secara otomatis menghasilkan pembungkus tipis di sekitar perpustakaan masih membuat Anda membangun perpustakaan inti Anda di sekitar API orang lain dan membangun tiruan - yang merupakan banyak upaya yang tidak dapat dihindari jika Anda bersikeras meninggalkan perpustakaan keluar dari tes Anda.
svidgen
1
@DocBrown Ini tidak masuk akal. "Apakah kelas masalah ini memiliki kelas alat atau pendekatan?" Iya. Tentu saja. Pengodean defensif dan tidak menjadi dogmatis tentang unit test ... seperti jawaban saya katakan. Jika Anda memang memiliki pembungkus tipis yang dihasilkan secara otomatis, berapa nilainya? ... Itu tidak akan mengizinkan Anda untuk menyuntikkan dependensi untuk pengujian, Anda masih harus melakukannya secara manual. Dan itu tidak akan memungkinkan Anda untuk menukar perpustakaan, karena Anda masih mengkode terhadap API perpustakaan ... itu hanya tidak langsung sekarang.
svidgen
9

Jangan unit menguji kode itu. Tuliskan tes integrasi sebagai gantinya. Dalam beberapa kasus, unit pengujian, mengejek, adalah membosankan dan menyakitkan. Parit tes unit dan tulis tes integrasi yang benar-benar membuat vendor berteriak.

Catatan, tes ini harus dijalankan setelah penyebaran sebagai aktivitas otomatis pasca penempatan. Mereka tidak dijalankan sebagai bagian dari unit test atau bagian dari proses build.

Terkadang tes integrasi lebih cocok daripada tes unit di bagian tertentu aplikasi Anda. Lingkaran yang harus dilalui seseorang untuk membuat kode "dapat diuji" kadang-kadang dapat merugikan.

Jon Raynor
sumber
2
Hal pertama yang saya pikirkan ketika saya membaca jawaban Anda adalah "tidak realistis untuk PDF, karena membandingkan file pada tingkat biner tidak memberi tahu Anda apa yang berubah atau jika perubahan itu bermasalah". Lalu saya menemukan posting SO yang lebih lama ini , menunjukkan bahwa itu sebenarnya mungkin berfungsi (jadi +1).
Doc Brown
@DocBrown alat di tautan SO tidak membandingkan PDF di tingkat biner. Ini membandingkan struktur dan isi halaman. Pdf internal structure: safaribooksonline.com/library/view/pdf-explained/9781449321581/…
linuxunil
1
@linuxunil: ya, saya tahu, seperti yang saya tulis, pertama saya pikir ... tapi kemudian saya menemukan solusi yang disebutkan di atas.
Doc Brown
oh .. saya mengerti .. mungkin 'itu benar-benar mungkin berhasil' di akhir jawaban Anda membuat saya bingung.
linuxunil
1

Seperti yang saya mengerti, diskusi ini berfokus pada peluang untuk otomatisasi pembuatan wrapper daripada membungkus ide dan pedoman implementasi. Saya akan mencoba abstrak dari ide karena sudah ada banyak hal di sini.

Saya melihat bahwa kami bermain-main dengan teknologi .NET, oleh karena itu kami memiliki kemampuan refleksi yang kuat di tangan kami. Anda dapat mempertimbangkan:

  1. Alat seperti .NET Wrapper Class Generator . Saya belum pernah menggunakan alat itu dan saya tahu itu beroperasi pada tumpukan teknologi warisan, tapi mungkin untuk kasus Anda itu akan cocok. Tentu saja, kualitas kode, dukungan untuk Inversi Ketergantungan dan Segregasi Antarmuka harus diselidiki secara terpisah. Mungkin ada alat lain seperti itu tetapi saya tidak banyak mencari.
  2. Menulis alat Anda sendiri yang akan mencari dalam rujukan referensi untuk metode / antarmuka publik dan melakukan pemetaan dan pembuatan kode. Sumbangan bagi komunitas akan lebih dari diterima!
  3. Jika .NET bukan kasing ... mungkin lihat di sini .

Saya percaya bahwa otomatisasi semacam itu mungkin merupakan titik dasar, tetapi bukan solusi akhir. Refactoring kode manual akan diperlukan ... atau dalam kasus terburuk redesign karena saya sepenuhnya setuju dengan apa yang ditulis svidgen:

Bangun aplikasi Anda terhadap antarmuka yang Anda inginkan; tidak menentang API pihak ke-3.

Tom3k
sumber
Oke, setidaknya gagasan bagaimana masalah itu bisa ditangani. Tapi tidak berdasarkan pengalaman sendiri untuk use case ini, saya kira?
Doc Brown
Pertanyaannya didasarkan pada pengalaman saya sendiri tentang domain rekayasa perangkat lunak (pembungkus, refleksi, di)! Mengenai alat - saya tidak menggunakannya seperti yang dinyatakan dalam asnwer.
tom3k
0

Ikuti panduan ini saat membuat perpustakaan pembungkus:

  • tampilkan hanya sebagian kecil dari perpustakaan pihak ketiga yang Anda butuhkan saat ini (dan perluas sesuai permintaan)
  • menjaga pembungkus setipis mungkin (tidak ada logika yang dimiliki di sana).
Rumen Georgiev
sumber
1
Hanya ingin tahu mengapa Anda mendapat 3 upvotes untuk sebuah postingan yang melewatkan poin pertanyaan.
Doc Brown