Mengapa banyak pengembang perangkat lunak melanggar prinsip terbuka / tertutup dengan memodifikasi banyak hal seperti mengganti nama fungsi yang akan merusak aplikasi setelah memutakhirkan?
Pertanyaan ini melompat ke kepala saya setelah versi cepat dan berkelanjutan di perpustakaan Bereaksi .
Setiap periode singkat saya melihat banyak perubahan dalam sintaks, nama komponen, ... dll
Contoh dalam versi React yang akan datang :
Peringatan Penghentian Baru
Perubahan terbesar adalah kami telah mengekstrak React.PropTypes dan React.createClass ke dalam paket mereka sendiri. Keduanya masih dapat diakses melalui objek Bereaksi utama, tetapi menggunakan salah satu akan mencatat peringatan penghentian satu kali ke konsol ketika dalam mode pengembangan. Ini akan memungkinkan optimasi ukuran kode di masa depan.
Peringatan ini tidak akan memengaruhi perilaku aplikasi Anda. Namun, kami menyadari bahwa mereka dapat menyebabkan beberapa frustrasi, terutama jika Anda menggunakan kerangka kerja pengujian yang memperlakukan console.error sebagai kegagalan.
- Apakah perubahan ini dianggap sebagai pelanggaran terhadap prinsip itu?
- Sebagai seorang pemula untuk sesuatu seperti Bereaksi , bagaimana saya mempelajarinya dengan perubahan cepat di perpustakaan (ini sangat membuat frustrasi)?
sumber
Jawaban:
Jawaban IMHO JacquesB, meskipun mengandung banyak kebenaran, menunjukkan kesalahpahaman mendasar dari OCP. Agar adil, pertanyaan Anda sudah mengungkapkan kesalahpahaman ini, juga - fungsi penamaan ulang merusak kompatibilitas , tetapi bukan OCP. Jika melanggar kompatibilitas tampaknya perlu (atau mempertahankan dua versi komponen yang sama agar tidak merusak kompatibilitas), OCP sudah rusak sebelumnya!
Seperti yang Jorg W Mittag katakan dalam komentarnya, prinsipnya tidak mengatakan "Anda tidak dapat mengubah perilaku komponen" - katanya, orang harus mencoba mendesain komponen dengan cara yang terbuka untuk digunakan kembali (atau diperluas) dalam beberapa hal, tanpa perlu modifikasi. Ini dapat dilakukan dengan memberikan "titik ekstensi" yang tepat, atau, sebagaimana disebutkan oleh @AntP, "dengan mendekomposisi struktur kelas / fungsi ke titik di mana setiap titik ekstensi alami ada di sana secara default." IMHO mengikuti OCP tidak memiliki kesamaan dengan "menjaga versi lama di sekitar tidak berubah untuk kompatibilitas mundur" ! Atau, mengutip komentar @ DerekElkin di bawah ini:
Pemrogram yang baik menggunakan pengalaman mereka untuk merancang komponen dengan mempertimbangkan titik ekstensi "benar" (atau - bahkan lebih baik - dengan cara tidak diperlukan titik ekstensi buatan). Namun, untuk melakukan ini dengan benar dan tanpa rekayasa ulang yang tidak perlu, Anda harus tahu sebelumnya bagaimana bentuk kasus penggunaan komponen Anda di masa depan. Bahkan programmer berpengalaman tidak dapat melihat masa depan dan mengetahui semua persyaratan yang akan datang sebelumnya. Dan itulah mengapa terkadang kompatibilitas ke belakang perlu dilanggar - tidak peduli berapa banyak titik ekstensi yang dimiliki komponen Anda, atau seberapa baik mengikuti OCP sehubungan dengan jenis persyaratan tertentu, akan selalu ada persyaratan yang tidak dapat diimplementasikan dengan mudah tanpa memodifikasi komponen.
sumber
Prinsip terbuka / tertutup memiliki manfaat, tetapi juga memiliki beberapa kelemahan serius.
Secara teori prinsip ini memecahkan masalah kompatibilitas ke belakang dengan membuat kode yang "terbuka untuk ekstensi tetapi ditutup untuk modifikasi". Jika suatu kelas memiliki beberapa persyaratan baru, Anda tidak pernah memodifikasi kode sumber dari kelas itu sendiri tetapi malah membuat subkelas yang hanya menimpa anggota yang tepat yang diperlukan untuk mengubah perilaku. Karenanya, semua kode yang ditulis terhadap versi asli kelas tidak terpengaruh, sehingga Anda dapat yakin bahwa perubahan Anda tidak merusak kode yang ada.
Pada kenyataannya, Anda dengan mudah berakhir dengan kode mengasapi dan kelas-kelas usang yang membingungkan. Jika tidak mungkin untuk mengubah beberapa perilaku komponen melalui ekstensi, maka Anda harus memberikan varian baru komponen dengan perilaku yang diinginkan, dan menjaga versi lama tidak berubah untuk kompatibilitas ke belakang.
Katakanlah Anda menemukan cacat desain mendasar di kelas dasar yang banyak mewarisi dari kelas. Katakanlah kesalahan adalah karena bidang pribadi dari jenis yang salah. Anda tidak dapat memperbaikinya dengan mengabaikan anggota. Pada dasarnya Anda harus mengganti seluruh kelas, yang berarti Anda akhirnya memperluas
Object
untuk memberikan kelas dasar alternatif - dan sekarang Anda juga harus memberikan alternatif untuk semua subclass, sehingga berakhir dengan hierarki objek yang digandakan, satu hierarki cacat, satu ditingkatkan . Tetapi Anda tidak dapat menghapus hierarki yang cacat (karena penghapusan kode adalah modifikasi), semua klien di masa depan akan terkena kedua hierarki tersebut.Sekarang jawaban teoretis untuk masalah ini adalah "desain saja dengan benar pertama kali". Jika kode diuraikan dengan sempurna, tanpa cacat atau kesalahan, dan dirancang dengan titik ekstensi yang disiapkan untuk semua kemungkinan perubahan persyaratan di masa depan, maka Anda menghindari kekacauan. Namun pada kenyataannya semua orang membuat kesalahan, dan tidak ada yang bisa memprediksi masa depan dengan sempurna.
Ambil sesuatu seperti .NET framework - masih membawa sekitar sekumpulan kelas koleksi yang dirancang sebelum obat generik diperkenalkan lebih dari satu dekade lalu. Ini tentu merupakan keuntungan bagi kompatibilitas ke belakang (Anda dapat meningkatkan kerangka kerja tanpa harus menulis ulang apa pun), tetapi juga membengkak kerangka kerja dan memberi pengembang sejumlah pilihan di mana banyak yang sudah usang.
Rupanya para pengembang React merasa tidak sepadan dengan biaya dalam kerumitan dan kode-gembung untuk secara ketat mengikuti prinsip terbuka / tertutup.
Alternatif pragmatis untuk membuka / menutup dikontrol depresiasi. Daripada melanggar kompatibilitas ke belakang dalam satu rilis, komponen lama disimpan untuk siklus rilis, tetapi klien diberitahu melalui peringatan kompiler bahwa pendekatan lama akan dihapus dalam rilis berikutnya. Ini memberi klien waktu untuk memodifikasi kode. Ini tampaknya menjadi pendekatan Bereaksi dalam kasus ini.
(Penafsiran saya tentang prinsip ini didasarkan pada Prinsip Terbuka-Terbuka oleh Robert C. Martin)
sumber
Saya akan menyebut prinsip buka / tutup sebagai ideal. Seperti semua cita-cita, itu memberikan sedikit pertimbangan untuk realitas pengembangan perangkat lunak. Seperti halnya semua cita-cita, mustahil untuk benar-benar mencapainya dalam praktik - seseorang hanya berusaha mendekati ideal itu sebaik mungkin.
Sisi lain dari cerita ini dikenal sebagai Golden Borgol. Borgol Emas adalah apa yang Anda dapatkan ketika Anda terlalu banyak mengandalkan prinsip terbuka / tertutup. Borgol Emas adalah apa yang terjadi ketika produk Anda yang tidak pernah rusak kompatibilitas tidak dapat tumbuh karena kesalahan masa lalu yang terlalu banyak telah dibuat.
Contoh terkenal ini ditemukan di manajer memori Windows 95. Sebagai bagian dari pemasaran untuk Windows 95, dinyatakan bahwa semua aplikasi Windows 3.1 akan berfungsi di Windows 95. Microsoft sebenarnya memperoleh lisensi untuk ribuan program untuk mengujinya di Windows 95. Salah satu kasus yang bermasalah adalah Sim City. Sim City sebenarnya memiliki bug yang menyebabkannya menulis ke memori yang tidak terisi. Di Windows 3.1, tanpa manajer memori "yang tepat", ini adalah kesalahan kecil. Namun, pada Windows 95, manajer memori akan menangkap ini dan menyebabkan kesalahan segmentasi. Solusinya? Di Windows 95, jika nama aplikasi Anda
simcity.exe
, OS akan benar-benar mengendurkan kendala manajer memori untuk mencegah kesalahan segmentasi!Masalah sebenarnya di balik cita-cita ini adalah konsep produk dan layanan yang dikupas. Tidak ada yang benar-benar melakukan yang satu atau yang lain. Semuanya berbaris di suatu tempat di wilayah abu-abu di antara keduanya. Jika Anda berpikir dari pendekatan yang berorientasi produk, buka / tutup terdengar seperti ideal. Produk Anda dapat diandalkan. Namun, ketika datang ke layanan, ceritanya berubah. Sangat mudah untuk menunjukkan bahwa dengan prinsip terbuka / tertutup, jumlah fungsionalitas yang harus didukung oleh tim Anda harus secara asimptotik mendekati tak terhingga, karena Anda tidak pernah dapat membersihkan fungsionalitas lama. Ini berarti tim pengembangan Anda harus mendukung semakin banyak kode setiap tahun. Akhirnya Anda mencapai titik puncaknya.
Sebagian besar perangkat lunak saat ini, terutama open source, mengikuti versi santai umum dari prinsip buka / tutup. Sangat umum untuk melihat terbuka / tertutup diikuti secara kasar untuk rilis kecil, tetapi ditinggalkan untuk rilis besar. Sebagai contoh, Python 2.7 berisi banyak "pilihan buruk" dari Python 2.0 dan 2.1 hari, tetapi Python 3.0 menyapu semuanya. (Juga, pergeseran dari Windows 95 basis kode untuk basis kode Windows NT ketika mereka merilis Windows 2000 pecah segala macam hal, tetapi tidak berarti kita tidak pernah harus berurusan dengan manajer memori memeriksa nama aplikasi untuk memutuskan perilaku!)
sumber
Jawaban Doc Brown paling dekat dengan akurat, jawaban lain menggambarkan kesalahpahaman dari Prinsip Tertutup Terbuka.
Secara eksplisit mengartikulasikan kesalahpahaman, tampaknya ada keyakinan bahwa OCP berarti bahwa Anda tidak harus membuat perubahan mundur kompatibel (atau bahkan setiap perubahan atau sesuatu sepanjang garis-garis ini.) The OCP adalah tentang merancang komponen sehingga Anda tidak perlu untuk buat perubahan pada mereka untuk memperluas fungsionalitasnya, terlepas dari apakah perubahan itu kompatibel atau tidak. Ada banyak alasan lain selain menambahkan fungsionalitas yang Anda dapat membuat perubahan pada komponen apakah mereka kompatibel ke belakang (mis. Refactoring atau optimasi) atau mundur tidak kompatibel (mis. Deprecating dan menghapus fungsionalitas). Anda dapat melakukan perubahan ini tidak berarti bahwa komponen Anda melanggar OCP (dan jelas bukan berarti Anda melanggar OCP).
Sungguh, ini bukan tentang kode sumber sama sekali. Pernyataan yang lebih abstrak dan relevan dari OCP adalah: "komponen harus memungkinkan untuk perpanjangan tanpa perlu melanggar batas abstraksi". Saya akan melangkah lebih jauh dan mengatakan rendisi yang lebih modern adalah: "komponen harus menegakkan batas abstraksinya tetapi memungkinkan untuk perpanjangan". Bahkan dalam artikel tentang OCP oleh Bob Martin ketika ia "menggambarkan" "tertutup untuk modifikasi" sebagai "kode sumber tidak dapat diganggu gugat", ia kemudian mulai berbicara tentang enkapsulasi yang tidak ada hubungannya dengan memodifikasi kode sumber dan segala sesuatu yang berkaitan dengan abstraksi batas-batas.
Jadi, premis yang salah dalam pertanyaan adalah bahwa OCP (dimaksudkan sebagai) pedoman tentang evolusi basis kode. OCP biasanya di-slogan sebagai "sebuah komponen harus terbuka untuk ekstensi dan tertutup untuk modifikasi oleh konsumen". Pada dasarnya, jika seorang konsumen komponen ingin menambahkan fungsionalitas ke komponen mereka harus dapat memperluas komponen lama ke yang baru dengan fungsi tambahan, tetapi mereka tidak dapat mengubah komponen yang lama.
OCP tidak mengatakan apa pun tentang pembuat komponen yang mengubah atau menghapus fungsionalitas. OCP tidak menganjurkan menjaga kompatibilitas bug selamanya. Anda, sebagai pembuat, tidak melanggar OCP dengan mengubah atau bahkan menghapus komponen. Anda, atau lebih tepatnya komponen yang Anda tulis, melanggar OCP jika satu-satunya cara konsumen dapat menambahkan fungsionalitas ke komponen Anda adalah dengan memutasikannya misalnya dengan menambal monyetatau memiliki akses ke kode sumber dan kompilasi ulang. Dalam banyak kasus, tak satu pun dari ini adalah pilihan bagi konsumen yang berarti jika komponen Anda tidak "terbuka untuk perpanjangan" mereka tidak beruntung. Mereka tidak bisa menggunakan komponen Anda untuk kebutuhan mereka. OCP berpendapat untuk tidak menempatkan konsumen perpustakaan Anda ke posisi ini, setidaknya sehubungan dengan beberapa kelas "ekstensi" yang dapat diidentifikasi. Bahkan ketika modifikasi dapat dilakukan pada kode sumber atau bahkan salinan utama dari kode sumber, yang terbaik adalah "berpura-pura" bahwa Anda tidak dapat memodifikasinya karena ada banyak konsekuensi negatif potensial untuk melakukannya.
Jadi untuk menjawab pertanyaan Anda: Tidak, ini bukan pelanggaran OCP. Tidak ada perubahan yang dibuat oleh penulis dapat menjadi pelanggaran terhadap OCP karena OCP tidak sebanding dengan perubahan. Perubahan, bagaimanapun, dapat membuat pelanggaran OCP, dan mereka dapat dimotivasi oleh kegagalan OCP di versi sebelumnya dari basis kode. OCP adalah properti dari sepotong kode tertentu, bukan sejarah evolusi basis kode.
Sebaliknya, kompatibilitas mundur adalah properti dari perubahan kode. Tidak masuk akal untuk mengatakan sebagian kode kompatibel atau tidak kompatibel. Masuk akal untuk berbicara tentang kompatibilitas mundur beberapa kode sehubungan dengan beberapa kode lama. Oleh karena itu, tidak pernah masuk akal untuk berbicara tentang potongan pertama dari beberapa kode yang kompatibel atau tidak. Potongan kode pertama dapat memuaskan atau gagal untuk memenuhi OCP, dan secara umum kita dapat menentukan apakah beberapa kode memenuhi OCP tanpa merujuk ke versi historis dari kode.
Adapun pertanyaan terakhir Anda, ini bisa dibilang off-topic untuk StackExchange secara umum sebagai terutama berdasarkan pendapat, tetapi singkatnya adalah selamat datang di teknologi dan khususnya JavaScript di mana dalam beberapa tahun terakhir fenomena yang Anda gambarkan telah disebut kelelahan JavaScript . (Jangan ragu-ragu ke google untuk menemukan berbagai artikel lain, beberapa menyindir, membicarakan hal ini dari berbagai perspektif.)
sumber
private
atau tidak. Jika seorang penulis membuatprivate
metodepublic
nanti, itu tidak berarti mereka telah melanggar kontrol akses, (1/2)private
sebelumnya. "Menghapus komponen yang diterbitkan jelas merupakan perubahan besar," adalah non sequitur. Entah komponen versi baru memenuhi OCP atau tidak, Anda tidak perlu riwayat basis kode untuk menentukan ini. Dengan logika Anda, saya tidak pernah bisa menulis kode yang memenuhi OCP. Anda mengkonfigurasikan kompatibilitas ke belakang, properti kode berubah, dengan OCP, properti kode. Komentar Anda sama masuk akal dengan mengatakan quicksort tidak kompatibel. (2/2)