Apa cara umum untuk menangani visibilitas di perpustakaan?

12

Pertanyaan tentang kapan menggunakan privat dan kapan menggunakan protected di kelas membuat saya berpikir. (Saya akan memperluas pertanyaan ini juga ke kelas dan metode akhir, karena terkait. Saya pemrograman di Jawa, tapi saya pikir ini relevan untuk setiap bahasa OOP)

Sais jawaban yang diterima:

Aturan praktis yang baik adalah: jadikan segala sesuatu senyaman mungkin.

Dan satu lagi:

  1. Jadikan semua kelas final kecuali jika Anda perlu subklas segera.
  2. Buat semua metode menjadi final kecuali Anda perlu membuat subkelas dan menimpanya segera.
  3. Buat semua parameter metode final kecuali Anda perlu mengubahnya di dalam tubuh metode, yang agak canggung sebagian besar kali.

Ini cukup mudah dan jelas, tetapi bagaimana jika saya kebanyakan menulis perpustakaan (Open Source on GitHub) alih-alih aplikasi?

Saya bisa menyebutkan banyak perpustakaan dan situasi, di mana

  • Perpustakaan diperluas dengan cara yang tidak pernah dipikirkan pengembang
  • Ini harus dilakukan dengan "magic loader kelas" dan peretasan lainnya karena kendala visibilitas
  • Perpustakaan digunakan dengan cara yang tidak dibuat untuknya dan fungsi fungsionalitas yang diperlukan "diretas"
  • Perpustakaan tidak dapat digunakan karena masalah kecil (bug, fungsionalitas yang hilang, perilaku "salah") yang tidak dapat diubah karena visibilitas berkurang
  • Masalah yang tidak dapat diperbaiki mengarah pada solusi besar, jelek dan bermasalah di mana mengesampingkan fungsi sederhana (yang bersifat pribadi atau final) dapat membantu

Dan saya benar-benar mulai memberi nama ini sampai pertanyaannya menjadi terlalu panjang dan saya memutuskan untuk menghapusnya.

Saya suka gagasan tidak memiliki lebih banyak kode daripada yang dibutuhkan, lebih banyak visibilitas daripada yang dibutuhkan, lebih banyak abstraksi daripada yang dibutuhkan. Dan ini mungkin berfungsi saat menulis aplikasi untuk pengguna akhir, di mana kode hanya digunakan oleh mereka yang menulisnya. Tetapi bagaimana ini bisa bertahan jika kode dimaksudkan untuk digunakan oleh pengembang lain, di mana tidak mungkin bahwa pengembang asli memikirkan setiap kasus penggunaan yang mungkin di muka dan perubahan / refaktor sulit / tidak mungkin dibuat?

Karena pustaka sumber terbuka besar bukanlah hal yang baru, apa cara paling umum untuk menangani visibilitas dalam proyek semacam itu dengan bahasa berorientasi objek?

piegames
sumber
mengingat bahwa Anda bertanya tentang open source, bahkan lebih tidak masuk akal untuk menekuk prinsip-prinsip pengkodean yang tepat untuk mengatasi masalah yang Anda daftarkan daripada sumber tertutup, hanya karena seseorang dapat berkontribusi perbaikan yang diperlukan langsung ke kode perpustakaan atau garpu dan membuat versi mereka sendiri dengan koreksi apa pun yang mereka inginkan
nyamuk
2
poin saya bukan tentang ini tetapi tentang referensi Anda ke open source tidak masuk akal dalam konteks ini. Saya bisa membayangkan bagaimana kebutuhan pragmatis dapat membenarkan penyimpangan dari prinsip-prinsip yang ketat dalam beberapa kasus (juga dikenal sebagai hutang teknis ) tetapi dari perspektif ini tidak masalah apakah kode ditutup atau open source. Atau lebih tepatnya itu berlawanan arah dari yang Anda bayangkan di sini karena kode menjadi open source dapat membuat kebutuhan ini kurang mendesak daripada tertutup karena menawarkan opsi tambahan untuk mengatasinya
agn
1
@ piegames: Saya sepenuhnya setuju untuk agas di sini, masalah yang Anda buat lebih besar kemungkinannya terjadi pada libs sumber tertutup - jika itu adalah lib OS dengan lisensi permisif, jika pengelola mengabaikan permintaan perubahan, seseorang dapat membayar lib dan ubah visibilitas sendiri, jika perlu.
Doc Brown
1
@piegames: Saya tidak mengerti pertanyaan Anda. "Java" adalah bahasa, bukan lib. Dan jika "perpustakaan open source kecil Anda" memiliki visibilitas yang terlalu ketat, memperluas visibilitas setelahnya biasanya tidak merusak kompatibilitas. Hanya sebaliknya.
Doc Brown

Jawaban:

15

Kebenaran yang disayangkan adalah bahwa banyak perpustakaan ditulis , bukan dirancang . Ini menyedihkan, karena sedikit pemikiran sebelumnya dapat mencegah banyak masalah di jalan.

Jika kita mulai mendesain perpustakaan, akan ada beberapa kasus penggunaan yang diantisipasi. Perpustakaan mungkin tidak memuaskan semua kasus penggunaan secara langsung, tetapi dapat berfungsi sebagai bagian dari solusi. Jadi perpustakaan perlu cukup fleksibel untuk beradaptasi.

Kendala adalah bahwa biasanya bukan ide yang baik untuk mengambil kode sumber perpustakaan dan memodifikasinya untuk menangani kasus penggunaan baru. Untuk pustaka berpemilik, sumbernya mungkin tidak tersedia, dan untuk pustaka sumber terbuka mungkin tidak diinginkan untuk mempertahankan versi bercabang. Mungkin tidak layak untuk menggabungkan adaptasi yang sangat spesifik ke dalam proyek hulu.

Di sinilah prinsip buka-tutup muncul: perpustakaan harus terbuka untuk ekstensi tanpa mengubah kode sumber. Itu tidak datang secara alami. Ini harus menjadi tujuan desain yang disengaja. Ada banyak teknik yang dapat membantu di sini, pola desain OOP klasik adalah beberapa di antaranya. Secara umum, kami menentukan kait di mana kode pengguna dapat dengan aman menyambungkan ke perpustakaan dan menambahkan fungsionalitas.

Hanya membuat setiap metode publik atau memungkinkan setiap kelas untuk subklas tidak cukup untuk mencapai ekstensibilitas. Pertama-tama, sangat sulit untuk memperluas perpustakaan jika tidak jelas di mana pengguna dapat menghubungkan ke perpustakaan. Misalnya, mengganti sebagian besar metode tidak aman karena metode kelas dasar ditulis dengan asumsi implisit. Anda benar-benar perlu merancang untuk diperpanjang.

Lebih penting lagi, begitu sesuatu menjadi bagian dari API publik, Anda tidak dapat mengambilnya kembali. Anda tidak dapat melakukan refactor tanpa merusak kode hilir. Keterbukaan prematur membatasi perpustakaan ke desain yang kurang optimal. Sebaliknya, menjadikan barang-barang internal sebagai hal pribadi tetapi menambahkan kait jika nanti dibutuhkan untuk mereka adalah pendekatan yang lebih aman. Meskipun itu adalah cara yang waras untuk mengatasi evolusi jangka panjang sebuah perpustakaan, ini tidak memuaskan bagi pengguna yang perlu menggunakan perpustakaan saat ini .

Jadi, apa yang terjadi? Jika ada rasa sakit yang signifikan dengan keadaan perpustakaan saat ini, pengembang dapat mengambil semua pengetahuan tentang kasus penggunaan aktual yang terakumulasi dari waktu ke waktu, dan menulis Versi 2 dari perpustakaan. Itu akan luar biasa! Ini akan memperbaiki semua bug yang ada di desain! Ini juga akan memakan waktu lebih lama dari yang diharapkan, dalam banyak kasus gagal. Dan jika versi baru sangat berbeda dengan versi lama, mungkin sulit untuk mendorong pengguna untuk bermigrasi. Anda kemudian tetap mempertahankan dua versi yang tidak kompatibel.

amon
sumber
Jadi saya perlu menambahkan kait untuk ekstensi karena hanya membuatnya publik / dapat diganti tidak cukup. Dan saya juga perlu memikirkan kapan harus merilis perubahan / API baru karena kompatibilitas ke belakang. Tapi bagaimana dengan visibilitas metode khusus?
piegames
@piegames Dengan visibilitas Anda memutuskan bagian mana yang publik (bagian dari API stabil Anda) dan bagian mana yang pribadi (dapat berubah). Jika seseorang mengelak dengan refleksi, itu masalah mereka ketika fitur itu rusak di masa depan. Ngomong-ngomong, titik ekstensi sering dalam bentuk metode yang dapat diganti. Tetapi ada perbedaan antara metode yang dapat diganti, dan metode yang dimaksudkan untuk diganti (lihat juga Pola Metode Template).
amon
8

Setiap kelas / metode publik dan dapat dikembangkan adalah bagian dari API Anda yang harus didukung. Membatasi yang ditetapkan ke subset yang wajar dari perpustakaan memungkinkan stabilitas yang paling dan membatasi jumlah hal yang bisa salah. Ini adalah keputusan manajemen (dan bahkan proyek OSS dikelola sampai tingkat tertentu) berdasarkan pada apa yang dapat Anda dukung secara wajar.

Perbedaan antara OSS dan sumber tertutup adalah bahwa kebanyakan orang berusaha membuat dan menumbuhkan komunitas di sekitar kode sehingga lebih dari satu orang yang memelihara perpustakaan. Yang mengatakan, ada sejumlah alat manajemen yang tersedia:

  • Milis membahas kebutuhan pengguna dan cara mengimplementasikan sesuatu
  • Sistem pelacakan masalah (masalah JIRA atau Git, dll.) Melacak bug dan permintaan fitur
  • Kontrol versi mengelola kode sumber.

Dalam proyek yang matang, apa yang akan Anda lihat adalah sesuatu seperti ini:

  1. Seseorang ingin melakukan sesuatu dengan perpustakaan yang awalnya tidak dirancang untuk dilakukan
  2. Mereka menambahkan tiket ke pelacakan masalah
  3. Tim dapat mendiskusikan masalah ini di milis atau di komentar, dan pemohon selalu diundang untuk bergabung dalam diskusi
  4. Perubahan API diterima dan diprioritaskan atau ditolak karena alasan tertentu

Pada saat itu, jika perubahan diterima tetapi pengguna ingin mempercepatnya diperbaiki, mereka dapat melakukan pekerjaan dan mengirimkan permintaan tarik atau tambalan (tergantung pada alat kontrol versi).

Tidak ada API yang statis. Namun pertumbuhan itu harus dibentuk dalam beberapa cara. Dengan menjaga segala sesuatu ditutup sampai ada kebutuhan yang diperlihatkan untuk membuka sesuatu, Anda menghindari reputasi kereta yang tidak stabil.

Berin Loritsch
sumber
1
Sepenuhnya setuju, dan saya telah berhasil mempraktikkan proses permintaan perubahan yang Anda buat untuk lib sumber tertutup pihak ke-3 juga, dan juga untuk lib sumber terbuka.
Doc Brown
Dalam pengalaman saya, bahkan perubahan kecil di perpustakaan kecil banyak pekerjaan (bahkan jika itu hanya untuk meyakinkan yang lain) dan mungkin perlu waktu (untuk menunggu rilis berikutnya jika Anda tidak mampu menggunakan snapshot sampai saat itu). Jadi ini jelas bukan pilihan bagi saya. Saya akan tertarik: apakah ada perpustakaan yang lebih besar (di GitHub) yang benar-benar menggunakan konsep itu?
piegames
Itu selalu banyak pekerjaan. Hampir setiap proyek yang saya sumbangkan memiliki proses yang mirip dengan itu. Kembali di hari Apache saya, kami mungkin mendiskusikan sesuatu selama berhari-hari karena kami bersemangat tentang apa yang kami buat. Kami tahu banyak orang akan menggunakan perpustakaan, jadi kami harus mendiskusikan hal-hal seperti apakah perubahan yang diusulkan akan merusak API, apakah fitur yang diusulkan layak, kapan kami harus melakukannya? dll
Berin Loritsch
0

Saya akan menulis ulang tanggapan saya karena tampaknya itu membuatku kesal dengan beberapa orang.

visibilitas properti kelas / metode tidak ada hubungannya dengan keamanan atau keterbukaan sumber.

Alasan mengapa visibilitas ada, adalah karena objek rentan terhadap 4 masalah spesifik:

  1. konkurensi

Jika Anda membuat modul Anda tidak terbungkus, maka pengguna Anda akan terbiasa mengubah status modul secara langsung. Ini berfungsi dengan baik di lingkungan utas tunggal, tetapi begitu Anda bahkan berpikir tentang menambahkan utas; Anda akan dipaksa untuk membuat negara menjadi privat dan menggunakan kunci / monitor bersama dengan getter dan setter yang membuat utas lain menunggu sumber daya, alih-alih berlomba dengannya. Ini berarti program pengguna Anda tidak akan berfungsi lagi karena variabel pribadi tidak dapat diakses dengan cara konvensional. Ini bisa berarti Anda membutuhkan banyak penulisan ulang.

Yang benar adalah bahwa lebih mudah untuk membuat kode dengan runtime tunggal dalam pikiran, dan kata kunci pribadi memungkinkan Anda untuk menambahkan kata kunci yang disinkronkan, atau beberapa kunci, dan kode pengguna Anda tidak akan rusak jika Anda merangkumnya dari awal. .

  1. Bantu mencegah pengguna memotret diri mereka sendiri dalam penggunaan antarmuka yang sederhana. Intinya, ini membantu Anda mengontrol invarian objek.

Setiap objek memiliki banyak hal yang diperlukan untuk menjadi kenyataan agar berada dalam keadaan konsisten. Sayangnya, hal-hal ini hidup di ruang yang terlihat klien karena mahal untuk memindahkan setiap objek ke prosesnya sendiri dan berbicara dengannya melalui pesan. Ini berarti bahwa sangat mudah bagi sebuah objek untuk crash keseluruhan program jika pengguna memiliki visibilitas penuh.

Ini tidak dapat dihindari, tetapi Anda dapat mencegah secara tidak sengaja menempatkan suatu objek ke kondisi tidak konsisten dengan membuat penutupan antarmuka atas layanannya yang mencegah crash tidak disengaja dengan hanya memungkinkan pengguna untuk berinteraksi dengan keadaan objek melalui antarmuka yang dibuat dengan cermat yang membuat program jauh lebih kuat . Ini tidak berarti pengguna tidak dapat dengan sengaja merusak invarian, tetapi jika mereka melakukannya, itu adalah klien mereka yang macet, yang harus mereka lakukan adalah me-restart program (data yang ingin Anda lindungi tidak boleh disimpan di sisi klien ).

Contoh bagus lainnya di mana Anda dapat meningkatkan kegunaan modul Anda adalah menjadikan konstruktor sebagai pribadi; karena jika konstruktor melempar pengecualian, itu akan mematikan program. Salah satu pendekatan malas untuk menyelesaikan ini adalah membuat konstruktor membuat kesalahan waktu kompilasi Anda bahwa Anda tidak dapat membangunnya kecuali itu dalam blok try / catch. Dengan menjadikan konstruktor sebagai pribadi, dan menambahkan metode pembuatan statis publik, Anda dapat membuat null buat metode jika gagal membuatnya, atau menggunakan fungsi panggil balik untuk menangani kesalahan, membuat program lebih ramah pengguna.

  1. Lingkup polusi

Banyak kelas memiliki banyak status dan metode dan mudah kewalahan mencoba menggulirnya; Banyak dari metode ini hanyalah noise visual seperti fungsi helper, state. membuat variabel dan metode pribadi membantu mengurangi polusi ruang lingkup dan membuatnya lebih mudah bagi pengguna untuk menemukan layanan yang mereka cari.

Intinya, ini memungkinkan Anda untuk pergi dengan memiliki fungsi pembantu di dalam kelas daripada di luar kelas; tanpa kontrol visibilitas tanpa mengganggu pengguna dengan sekelompok layanan yang seharusnya tidak pernah digunakan pengguna, sehingga Anda bisa lolos dengan memecah metode menjadi banyak metode pembantu (meskipun itu masih akan mencemari ruang lingkup Anda, tetapi bukan pengguna).

  1. terikat pada dependensi

Antarmuka yang dibuat dengan baik dapat menyembunyikan basis data internal / windows / pencitraan yang tergantung pada untuk melakukan tugasnya, dan jika Anda ingin mengubah ke database lain / sistem windowing lain / perpustakaan pencitraan lain, Anda dapat menjaga antarmuka yang sama dan pengguna tidak akan memperhatikan.

Di sisi lain, jika Anda tidak melakukan ini, Anda dapat dengan mudah jatuh ke dalam membuatnya tidak mungkin untuk mengubah dependensi Anda, karena mereka terbuka, dan kode bergantung padanya. Dengan sistem yang cukup besar, biaya migrasi menjadi tidak terjangkau, sedangkan enkapsulasi dapat melindungi pengguna klien yang berperilaku baik dari keputusan masa depan untuk menukar ketergantungan.

Dmitry
sumber
1
"Tidak ada gunanya menyembunyikan apa pun" - lalu mengapa berpikir tentang enkapsulasi? Dalam banyak konteks refleksi memang membutuhkan hak istimewa khusus.
Frank Hileman
Anda berpikir tentang enkapsulasi karena memberi Anda ruang bernapas saat mengembangkan modul Anda, dan mengurangi kemungkinan penyalahgunaan. Misalnya, jika Anda memiliki 4 utas yang memodifikasi keadaan internal kelas secara langsung, itu akan dengan mudah menyebabkan masalah, sementara menjadikan variabel pribadi, mendorong pengguna untuk menggunakan metode publik untuk memanipulasi keadaan dunia, yang dapat menggunakan monitor / kunci untuk mencegah masalah. . Ini adalah satu-satunya manfaat nyata enkapsulasi.
Dmitry
Menyembunyikan barang-barang demi keamanan adalah cara mudah untuk membuat desain di mana Anda akhirnya harus memiliki lubang di api Anda. Contoh yang baik dari ini adalah aplikasi multi-dokumen, di mana Anda memiliki banyak kotak alat, dan banyak jendela dengan jendela kecil. Jika Anda menjadi gila saat enkapsulasi, Anda akhirnya akan memiliki situasi di mana menggambar sesuatu pada satu dokumen, Anda harus meminta jendela untuk meminta dokumen bagian dalam untuk meminta dokumen bagian dalam untuk meminta dokumen bagian dalam untuk mengajukan permintaan untuk menggambar sesuatu dan membatalkan konteksnya. Jika sisi klien ingin bermain dengan sisi klien, Anda tidak dapat mencegahnya.
Dmitry
OK itu lebih masuk akal, meskipun keamanan dapat dicapai melalui kontrol akses jika lingkungan mendukungnya, dan itu adalah salah satu tujuan awal dalam desain bahasa OO. Anda juga mempromosikan enkapsulasi dan mengatakan jangan menggunakannya secara bersamaan; agak membingungkan.
Frank Hileman
Saya tidak pernah bermaksud untuk tidak menggunakannya; Maksud saya jangan menggunakannya untuk keamanan; gunakan secara strategis untuk meningkatkan pengalaman pengguna Anda dan memberi Anda lingkungan pengembangan yang lebih halus. Maksud saya adalah tidak ada hubungannya dengan keamanan atau keterbukaan sumber. Objek sisi klien secara definisi rentan terhadap introspeksi, dan memindahkannya keluar dari ruang proses pengguna membuat hal-hal yang tidak terenkapsulasi sama-sama tidak dapat diakses seperti yang dienkapsulasi.
Dmitry