Apa artinya ketika seseorang mengatakan "Enkapsulasi apa yang bervariasi"?

25

Salah satu prinsip OOP yang saya temui adalah: -Merumuskan apa yang bervariasi.

Saya mengerti apa arti harfiah dari frasa tersebut, yaitu menyembunyikan apa yang berbeda-beda. Namun, saya tidak tahu persis bagaimana itu berkontribusi pada desain yang lebih baik. Adakah yang bisa menjelaskannya menggunakan contoh yang baik?

Haris Ghauri
sumber
Lihat en.wikipedia.org/wiki/Encapsulation_(computer_programming) yang menjelaskannya dengan baik. Saya pikir "apa yang bervariasi" tidak benar karena Anda juga kadang-kadang harus merangkum konstanta.
qwerty_so
I don't know how exactly would it contribute to a better designEnkapsulasi detail adalah tentang kopling longgar antara "model" dan detail implementasi. Semakin kurang terikat adalah "model" untuk detail implementasi, semakin fleksibel solusinya. Dan itu membuatnya lebih mudah untuk mengembangkannya. "Abstrak sendiri dari detail".
Laiv
@Laiv Jadi "bervariasi" mengacu pada apa yang berkembang selama siklus hidup perangkat lunak Anda atau perubahan apa selama eksekusi program Anda atau keduanya?
Haris Ghauri
2
@HarisGhauri keduanya. Kelompokkan bersama apa yang bervariasi bersama. Isolasikan apa yang bervariasi secara independen. Curiga terhadap apa yang Anda anggap tidak akan pernah berubah.
candied_orange
1
@laiv berpikir "abstrak" adalah poin yang bagus. Dapat terasa luar biasa untuk melakukan itu. Dalam satu objek Anda dianggap memiliki satu tanggung jawab. Hal yang menyenangkan tentang itu adalah Anda hanya perlu berpikir hati-hati tentang satu hal di sini. Ketika detail dari sisa masalah adalah masalah orang lain, hal itu membuat segalanya menjadi lebih mudah.
candied_orange

Jawaban:

30

Anda dapat menulis kode yang terlihat seperti ini:

if (pet.type() == dog) {
  pet.bark();
} else if (pet.type() == cat) {
  pet.meow();
} else if (pet.type() == duck) {
  pet.quack()
}

atau Anda dapat menulis kode yang terlihat seperti ini:

pet.speak();

Jika apa yang dienkapsulasi berbeda-beda maka Anda tidak perlu khawatir. Anda hanya khawatir tentang apa yang Anda butuhkan dan apa pun yang Anda gunakan angka bagaimana melakukan apa yang benar-benar Anda butuhkan berdasarkan apa yang bervariasi.

Meringkas apa yang bervariasi dan Anda tidak perlu menyebarkan kode di sekitar yang peduli tentang apa yang bervariasi. Anda hanya mengatur hewan peliharaan menjadi tipe tertentu yang tahu bagaimana berbicara sebagai tipe itu dan setelah itu Anda bisa melupakan jenis mana dan memperlakukannya seperti hewan peliharaan. Anda tidak perlu bertanya tipe yang mana.

Anda mungkin berpikir tipe encapsulated karena seorang pengambil diperlukan untuk mengaksesnya. Bukan saya. Getter's tidak benar-benar merangkum. Mereka hanya menguap ketika seseorang merusak enkapsulasi Anda. Mereka adalah dekorator yang bagus seperti kait berorientasi aspek yang paling sering digunakan sebagai kode debugging. Tidak peduli bagaimana Anda mengirisnya, Anda masih mengekspos tipenya.

Anda mungkin melihat contoh ini dan berpikir saya sedang mengkonfimasikan polimorfisme dan enkapsulasi. Bukan saya. Saya menyatukan "apa yang bervariasi" dan "detail".

Fakta bahwa hewan peliharaan Anda adalah anjing adalah detail. Yang mungkin berbeda untuk Anda. Yang mungkin tidak. Namun yang pasti berbeda dari orang ke orang. Kecuali kami percaya perangkat lunak ini hanya akan digunakan oleh pecinta anjing, pintar memperlakukan anjing sebagai detail dan merangkumnya. Dengan cara itu beberapa bagian dari sistem sangat tidak menyadari anjing dan tidak akan terkena dampak ketika kita bergabung dengan "beo adalah kita".

Pisahkan, pisahkan, dan sembunyikan detail dari sisa kode. Jangan biarkan pengetahuan detail menyebar melalui sistem Anda dan Anda akan mengikuti "merangkum apa yang bervariasi" dengan baik.

candied_orange
sumber
3
Itu sangat aneh. "Enkapsulasi apa yang bervariasi" bagi saya berarti menyembunyikan perubahan status, mis. Tidak pernah memiliki variabel global. Tetapi jawaban Anda masuk akal juga, bahkan jika itu terasa lebih merupakan jawaban untuk polimorfisme daripada enkapsulasi :)
David Arno
2
@DavidArno Polimorfisme adalah salah satu cara untuk membuat ini bekerja. Saya bisa saja menjejalkan struktur jika menjadi hewan peliharaan dan semuanya akan terlihat bagus di sini berkat enkapsulasi hewan peliharaan. Tapi itu hanya akan memindahkan kekacauan daripada membersihkannya.
candied_orange
1
"Enkapsulasi apa yang bervariasi" bagi saya berarti menyembunyikan perubahan status . Tidak, tidak. Saya suka komentar CO. Jawaban Derick Elkin masuk lebih dalam, membacanya lebih dari satu kali. Seperti @ JacquesB mengatakan "Prinsip ini sebenarnya cukup dalam"
radarbob
16

"Bervariasi" di sini berarti "dapat berubah seiring waktu karena perubahan persyaratan". Ini adalah prinsip desain inti: Untuk memisahkan dan mengisolasi bagian-bagian kode atau data yang mungkin harus diubah secara terpisah di masa mendatang. Jika satu persyaratan berubah, idealnya hanya mengharuskan kami untuk mengubah kode terkait di satu tempat. Tetapi jika basis kode dirancang dengan buruk, yaitu sangat saling berhubungan dan logika untuk persyaratan tersebar di banyak tempat, maka perubahan akan sulit dan memiliki risiko tinggi menyebabkan efek yang tidak terduga.

Katakanlah Anda memiliki aplikasi yang menggunakan penghitungan pajak penjualan di banyak tempat. Jika tarif pajak penjualan berubah, apa yang Anda inginkan:

  • tarif pajak penjualan adalah literal hardcode di mana-mana dalam aplikasi tempat pajak penjualan dihitung.

  • tarif pajak penjualan adalah konstanta global, yang digunakan di mana-mana dalam aplikasi tempat pajak penjualan dihitung.

  • ada satu metode yang disebut calculateSalesTax(product)yang merupakan satu-satunya tempat tarif pajak penjualan digunakan.

  • tarif pajak penjualan ditentukan dalam file konfigurasi atau bidang basis data.

Karena tarif pajak penjualan dapat berubah karena keputusan politik yang independen dari persyaratan lain, kami lebih suka mengisolasi dalam konfigurasi, sehingga dapat diubah tanpa memengaruhi kode apa pun. Tetapi dapat dibayangkan logika untuk menghitung pajak penjualan dapat berubah, mis. Tarif yang berbeda untuk produk yang berbeda, jadi kami juga ingin agar logika perhitungannya dienkapsulasi. Konstanta global mungkin tampak seperti ide yang baik, tetapi sebenarnya buruk, karena mungkin mendorong penggunaan pajak penjualan tempat yang berbeda dalam program daripada di satu tempat.

Sekarang perhatikan konstanta lain, Pi, yang juga digunakan di banyak tempat dalam kode. Apakah prinsip desain yang sama berlaku? Tidak, karena Pi tidak akan berubah. Mengekstraknya ke file konfigurasi atau bidang database tidak hanya menambah kerumitan yang tidak perlu (dan semua hal lain dianggap sama, kami lebih suka kode yang paling sederhana). Masuk akal untuk menjadikannya sebuah konstanta global daripada hardcode di beberapa tempat untuk menghindari ketidakkonsistenan dan meningkatkan keterbacaan.

Intinya adalah, jika kita hanya melihat bagaimana program itu bekerja sekarang , tarif pajak penjualan dan Pi adalah setara, keduanya adalah konstanta. Hanya ketika kita mempertimbangkan apa yang mungkin berbeda di masa depan , kita menyadari bahwa kita harus memperlakukan mereka secara berbeda dalam desain.

Prinsip ini sebenarnya cukup dalam, karena itu berarti Anda harus melihat melampaui apa yang seharusnya dilakukan basis kode hari ini , dan juga mempertimbangkan kekuatan eksternal yang dapat menyebabkannya berubah, dan bahkan memahami berbagai pemangku kepentingan di belakang persyaratan.

JacquesB
sumber
2
Pajak adalah contoh yang bagus. Hukum dan Pajak dapat berubah dari satu hari ke hari lainnya. Jika Anda menerapkan sistem deklarasi pajak Anda sangat terikat dengan perubahan semacam ini. Juga berubah dari satu Lokasi ke lainnya (negara, provinsi, ...)
Laiv
"Pi tidak akan berubah" membuatku tertawa. Memang benar, Pi tidak mungkin berubah, tetapi seandainya Anda tidak diizinkan menggunakannya lagi? Jika beberapa orang memiliki cara mereka, Pi akan ditinggalkan. Seandainya itu menjadi persyaratan? Semoga Anda memiliki hari Tau yang bahagia . Bagus jawaban BTW. Memang dalam.
candied_orange
14

Kedua jawaban saat ini tampaknya hanya mencapai sasaran, dan mereka berfokus pada contoh yang mengaburkan gagasan inti. Ini juga bukan (semata-mata) prinsip OOP tetapi prinsip desain perangkat lunak secara umum.

Hal yang "bervariasi" dalam frasa ini adalah kode. Christophe benar dalam mengatakan bahwa biasanya sesuatu yang mungkin berbeda, itulah yang sering Anda antisipasi . Tujuannya adalah melindungi diri Anda dari perubahan kode di masa mendatang. Ini terkait erat dengan pemrograman terhadap suatu antarmuka . Namun, Christophe salah untuk membatasi ini pada "detail implementasi". Bahkan, nilai nasihat ini sering kali disebabkan oleh perubahan persyaratan .

Ini hanya secara tidak langsung terkait dengan keadaan enkapsulasi, yang saya yakini sedang dipikirkan oleh David Arno. Nasihat ini tidak selalu (tetapi sering) menyarankan keadaan enkapsulasi, dan saran ini juga berlaku untuk objek yang tidak dapat diubah. Faktanya, konstanta penamaan hanyalah bentuk (sangat dasar) dari enkapsulasi yang bervariasi.

CandiedOrange secara eksplisit mengonfigurasi "apa yang bervariasi" dengan "perincian". Ini hanya sebagian benar. Saya setuju bahwa kode apa pun yang bervariasi adalah "perincian" dalam arti tertentu, tetapi "perincian" mungkin tidak berbeda (kecuali jika Anda mendefinisikan "perincian" untuk membuat tautologis ini). Mungkin ada alasan untuk merangkum detail yang tidak beragam, tetapi diktum ini bukan satu. Secara kasar, jika Anda sangat yakin bahwa "anjing", "kucing", dan "bebek" akan menjadi satu-satunya jenis yang perlu Anda tangani, maka diktum ini tidak menyarankan Refactoring yang dilakukan CandiedOrange.

Contoh Casting CandiedOrange dalam konteks yang berbeda, anggap kita memiliki bahasa prosedural seperti C. Jika saya memiliki beberapa kode yang berisi:

if (pet.type() == dog) {
  pet.bark();
} else if (pet.type() == cat) {
  pet.meow();
} else if (pet.type() == duck) {
  pet.quack()
}

Saya mungkin berharap sepotong kode ini akan berubah di masa depan. Saya dapat "merangkum" itu hanya dengan mendefinisikan prosedur baru:

void speak(pet) {
  if (pet.type() == dog) {
    pet.bark();
  } else if (pet.type() == cat) {
    pet.meow();
  } else if (pet.type() == duck) {
    pet.quack()
  }
}

dan menggunakan prosedur baru ini alih-alih blok kode (yaitu refactoring "metode ekstrak"). Pada titik ini menambahkan jenis "sapi" atau apa pun yang hanya perlu memperbarui speakprosedur. Tentu saja, dalam bahasa OO Anda dapat memanfaatkan pengiriman dinamis seperti disinggung oleh jawaban CandiedOrange. Ini akan terjadi secara alami jika Anda mengakses petmelalui antarmuka. Menghilangkan logika kondisional melalui pengiriman dinamis adalah masalah ortogonal yang merupakan bagian dari mengapa saya membuat tafsiran prosedural ini. Saya juga ingin menekankan bahwa ini tidak memerlukan fitur khusus untuk OOP. Bahkan dalam bahasa OO, merangkum apa yang bervariasi tidak berarti kelas atau antarmuka baru perlu dibuat.

Sebagai contoh yang lebih tipikal (yang lebih dekat tetapi tidak cukup OO), katakanlah kami ingin menghapus duplikat dari daftar. Katakanlah kita menerapkannya dengan mengulangi daftar pencatatan item yang telah kita lihat sejauh ini di daftar lain dan menghapus item yang telah kita lihat. Masuk akal untuk berasumsi bahwa kami mungkin ingin mengubah cara kami melacak item yang dilihat mungkin, setidaknya, untuk alasan kinerja. Diktum untuk merangkum apa yang bervariasi menunjukkan bahwa kita harus membangun tipe data abstrak untuk mewakili set item yang terlihat. Algoritme kami sekarang ditentukan berdasarkan tipe data Set abstrak ini, dan jika kami memutuskan untuk beralih ke pohon pencarian biner, algoritma kami tidak perlu diubah atau dirawat. Dalam bahasa OO, kami dapat menggunakan kelas atau antarmuka untuk menangkap tipe data abstrak ini. Dalam bahasa seperti SML / O '

Untuk contoh yang didorong oleh persyaratan, misalkan Anda perlu memvalidasi beberapa bidang terkait dengan beberapa logika bisnis. Meskipun Anda mungkin memiliki persyaratan khusus sekarang, Anda sangat curiga bahwa mereka akan berkembang. Anda dapat merangkum logika saat ini dalam prosedur / fungsi / aturan / kelasnya sendiri.

Meskipun ini adalah masalah ortogonal yang bukan bagian dari "merangkum apa yang bervariasi", sering alami untuk abstrak, yang diparameterisasi oleh, logika yang sekarang dienkapsulasi. Ini biasanya mengarah pada kode yang lebih fleksibel dan memungkinkan logika diubah dengan mengganti dalam implementasi alternatif daripada memodifikasi logika yang dienkapsulasi.

Derek Elkins
sumber
Oh ironi manis yang pahit. Ya, ini bukan semata-mata masalah OOP. Anda memergoki saya membiarkan detail paradigma bahasa mencemari jawaban saya dan menghukum saya dengan tepat dengan "memvariasikan" paradigma itu.
candied_orange
"Bahkan dalam bahasa OO, merangkum apa yang bervariasi tidak berarti kelas atau antarmuka baru perlu dibuat" - sulit untuk membayangkan situasi di mana tidak membuat kelas atau antarmuka baru tidak akan melanggar SRP
taurelas
11

"Enkapsulasi apa yang bervariasi" mengacu pada menyembunyikan detail implementasi yang dapat berubah dan berkembang.

Contoh:

Sebagai contoh, misalkan kelas Coursemelacak Studentsyang dapat mendaftar () Anda bisa menerapkannya dengan LinkedListdan mengekspos wadah untuk memungkinkan iterasi di atasnya:

class Course { 
    public LinkedList<Student> Atendees; 
    public bool register (Student s);  
    ...
}

Tapi ini bukan ide yang baik:

  • Pertama, orang bisa kekurangan perilaku yang baik dan menggunakannya sebagai swalayan, langsung menambahkan siswa ke daftar, tanpa melalui metode register ().
  • Tetapi yang lebih menyebalkan: ini menciptakan ketergantungan "menggunakan kode" ke detail implementasi bagian dalam dari kelas yang digunakan. Ini mungkin mencegah evolusi kelas di masa depan, misalnya jika Anda lebih suka menggunakan array, vektor, peta dengan nomor kursi, atau struktur data persisten Anda sendiri.

Jika Anda merangkum apa yang bervariasi (atau lebih tepatnya mengatakan, apa yang mungkin berbeda), Anda tetap memiliki kebebasan untuk menggunakan kode dan kelas yang dienkapsulasi untuk berevolusi satu sama lain sendiri. Inilah sebabnya mengapa ini merupakan prinsip penting dalam OOP.

Bacaan tambahan:

Christophe
sumber