Saya agak bingung tentang bagaimana prinsip Open Closed dapat diterapkan dalam kehidupan nyata. Persyaratan dalam setiap bisnis berubah dari waktu ke waktu. Menurut prinsip Open-Closed Anda harus memperluas kelas alih-alih memodifikasi kelas yang ada. Bagi saya setiap kali memperpanjang kelas sepertinya tidak praktis untuk memenuhi persyaratan. Biarkan saya memberi contoh dengan sistem pemesanan kereta.
Dalam sistem pemesanan kereta akan ada objek Tiket. Mungkin ada berbagai jenis tiket Tiket Reguler, Tiket Konsesi dll. Tiket adalah kelas abstrak dan RegularTicket dan ConcessionTickets adalah kelas konkret. Karena semua tiket akan memiliki metode PrintTicket yang umum, maka itu ditulis dalam Tiket yang merupakan kelas abstrak dasar. Katakanlah ini bekerja dengan baik selama beberapa bulan. Sekarang persyaratan baru datang, yang menyatakan untuk mengubah format tiket. Mungkin beberapa bidang lagi ditambahkan pada tiket yang dicetak atau mungkin formatnya diubah. Untuk memenuhi persyaratan ini, saya memiliki opsi berikut
- Ubah metode PrintTicket () di kelas abstrak Tiket. Tetapi ini akan melanggar prinsip Open-Closed.
- Mengganti metode PrintTicket () di kelas anak tetapi ini akan menduplikasi logika pencetakan yang melanggar prinsip KERING (Jangan ulangi sendiri).
Jadi pertanyaannya adalah
- Bagaimana saya dapat memenuhi persyaratan bisnis di atas tanpa melanggar prinsip Terbuka / Tertutup.
- Kapan kelas seharusnya ditutup untuk modifikasi? Apa kriteria untuk mempertimbangkan kelas ditutup untuk modifikasi? Apakah setelah implementasi awal kelas atau mungkin setelah penyebaran pertama dalam produksi atau mungkin sesuatu yang lain.
sumber
Jawaban:
Ini tergantung pada cara
PrintTicket
penerapannya. Jika metode perlu mempertimbangkan informasi dari subclass, ia perlu menyediakan cara bagi mereka untuk memberikan informasi tambahan.Selain itu, Anda dapat mengganti tanpa mengulangi kode Anda juga. Misalnya, jika Anda memanggil metode kelas dasar Anda dari implementasi, Anda menghindari pengulangan:
Pola Metode Templat menyediakan opsi ketiga: terapkan
PrintTicket
di kelas dasar, dan bergantung pada kelas turunan untuk menyediakan perincian tambahan sesuai kebutuhan.Berikut adalah contoh menggunakan hierarki kelas Anda:
Bukan kelas yang harus ditutup untuk modifikasi, tetapi antarmuka kelas itu (maksud saya "antarmuka" dalam arti luas, yaitu kumpulan metode dan properti dan perilaku mereka, bukan konstruksi bahasa). Kelas menyembunyikan implementasinya, sehingga pemilik kelas dapat memodifikasinya kapan saja, selama perilakunya yang terlihat secara eksternal tetap tidak berubah.
Antarmuka kelas harus tetap ditutup setelah pertama kali Anda menerbitkannya untuk penggunaan eksternal. Kelas penggunaan internal tetap terbuka untuk refactoring selamanya, karena Anda dapat menemukan dan memperbaiki semua penggunaannya.
Terlepas dari kasus-kasus paling sederhana, tidaklah praktis untuk mengharapkan bahwa hierarki kelas Anda akan mencakup semua skenario penggunaan yang mungkin setelah sejumlah iterasi refactoring. Persyaratan tambahan yang menyerukan metode yang sama sekali baru di kelas dasar datang secara teratur, sehingga kelas Anda tetap terbuka untuk modifikasi oleh Anda selamanya.
sumber
TicketPrinter
kelas yang diteruskan ke konstruktor.Mari kita mulai dengan garis sederhana prinsip Terbuka-tertutup -
"Open for extension Closed for modification"
. Pertimbangkan masalah Anda, saya akan merancang kelas-kelas sehingga Tiket dasar akan bertanggung jawab untuk mencetak informasi Tiket umum dan jenis Tiket lainnya akan bertanggung jawab untuk mencetak format mereka sendiri di atas pencetakan dasar.Dengan cara ini Anda menegakkan jenis tiket baru yang akan diperkenalkan dalam sistem akan menerapkan fitur pencetakan sendiri di atas informasi pencetakan tiket dasar. Tiket Pangkalan sekarang ditutup untuk modifikasi tetapi terbuka untuk perpanjangan dengan menyediakan metode tambahan untuk jenis turunannya.
sumber
Masalahnya bukan bahwa Anda melanggar prinsip Terbuka / Tertutup, masalahnya adalah bahwa Anda melanggar Prinsip Tanggung Jawab Tunggal.
Ini secara harfiah adalah contoh buku sekolah dari masalah SRP, atau seperti yang dinyatakan wikipedia :
Kelas-kelas Tiket yang berbeda dapat berubah karena Anda menambahkan informasi baru kepada mereka. Pencetakan tiket dapat berubah karena Anda memerlukan tata letak baru, karenanya Anda harus memiliki kelas TicketPrinter yang menerima tiket sebagai input.
Kelas Tiket Anda kemudian dapat mengimplementasikan antarmuka yang berbeda untuk menyediakan tipe data yang berbeda atau menyediakan semacam templat data.
sumber
Ini tidak melanggar prinsip buka-tutup. Kode berubah sepanjang waktu, itu sebabnya SOLID penting, sehingga kode tetap dapat dipelihara, fleksibel, dapat diubah. Tidak ada yang salah dengan mengubah kode.
OCP lebih banyak tentang kelas eksternal yang tidak dapat merusak fungsionalitas kelas yang dimaksudkan.
Sebagai contoh, saya memiliki kelas
A
yang mengambil String di konstruktor, dan memverifikasi String ini memiliki format tertentu dengan memanggil metode untuk memverifikasi string. Metode verifikasi ini juga harus dapat digunakan oleh klienA
, jadi dalam implementasi naif, itu dibuatpublic
.Sekarang saya dapat 'mematahkan' kelas ini dengan mewarisi darinya dan mengganti metode verifikasi untuk selalu kembali
true
. Karena polimorfisme, saya masih dapat menggunakan subkelas saya di mana saja di mana saya dapat menggunakanA
, mungkin membuat perilaku yang tidak diinginkan dalam program saya.Sepertinya ini adalah tindakan jahat yang harus dilakukan, tetapi dalam basis kode yang lebih kompleks, ini mungkin dilakukan sebagai 'kesalahan jujur'. Untuk menyesuaikan dengan OCP, kelas
A
harus dirancang sedemikian rupa sehingga membuat kesalahan ini tidak mungkin.Saya bisa melakukan ini dengan, misalnya, membuat metode verifikasi
final
.sumber
Warisan bukan satu-satunya mekanisme yang dapat digunakan untuk memenuhi persyaratan OCP, dan dalam banyak kasus saya berpendapat bahwa itu jarang yang terbaik - biasanya ada cara yang lebih baik, selama Anda berencana sedikit ke depan untuk mempertimbangkan jenis perubahan yang mungkin Anda butuhkan.
Dalam contoh ini, saya berpendapat bahwa jika Anda berharap untuk sering mendapatkan perubahan format pada sistem pencetakan tiket Anda (dan itu sepertinya taruhan yang cukup aman bagi saya), menggunakan sistem templating akan sangat masuk akal. Sekarang, kita tidak perlu menyentuh kode sama sekali: yang harus kita lakukan untuk memenuhi permintaan ini adalah mengubah templat (selama sistem templat Anda dapat mengakses semua data yang diperlukan, toh).
Pada kenyataannya, suatu sistem tidak pernah dapat sepenuhnya ditutup terhadap modifikasi, dan bahkan untuk menutupnya akan membutuhkan sejumlah besar upaya yang sia-sia, jadi Anda harus membuat keputusan untuk menilai perubahan seperti apa yang mungkin terjadi, dan menyusun kode sesuai dengan itu.
sumber