Jika suatu variabel memiliki pengambil dan penyetel, haruskah itu publik?

23

Saya memiliki kelas dengan variabel yang bersifat pribadi dan kelas memiliki pengambil dan penyetel untuk variabel itu. Mengapa tidak menjadikan variabel itu publik?

Satu-satunya kasus saya pikir Anda harus menggunakan getter dan setter adalah jika Anda perlu melakukan beberapa operasi selain set atau get. Contoh:

void my_class::set_variable(int x){
   /* Some operation like updating a log */
   this->variable = x;
}
Oni
sumber
10
Ada setter dan setter untuk melindungi. Suatu hari Anda mungkin ingin melakukan this->variable = x + 5, atau memanggil UpdateStatisticsfungsi dalam setter, dan dalam kasus-kasus itu classinstancea->variable = 5akan menyebabkan masalah.
Coder
1
Contoh lain adalah: set_inch, set_centimeter, get_inch, get_centimeter, dengan beberapa tindakan seram.
rwong
Yap, getter dan setter membantu Anda mengontrol variabel instan Anda dengan cara yang Anda inginkan agar mereka dikendalikan.
developerdoug
7
@ Kode: Anda selalu dapat menambahkan pengambil / penyetel Anda ketika kebutuhan itu muncul? Atau apakah ini sesuatu yang tidak dapat Anda lakukan dengan mudah di C ++ (pengalaman saya kebanyakan adalah Delphi)?
Marjan Venema
Pertanyaan ini: programmers.stackexchange.com/questions/21802/… mungkin menarik.
Winston Ewert

Jawaban:

21

Pernahkah Anda mendengar tentang properti?

Properti adalah bidang yang memiliki aksesor "bawaan" (getter dan setter). Java, misalnya, tidak memiliki properti, tetapi disarankan untuk menulis getter dan setter ke bidang pribadi. C # memiliki properti.

Jadi, mengapa kita membutuhkan getter dan setter? Pada dasarnya kita membutuhkannya untuk melindungi / melindungi lapangan. Misalnya, Anda tidak mengakses bidang dalam referensi memori, Anda sedang mengakses metode yang kemudian akan mengubah bidang (referensi). Metode itu mampu melakukan beberapa operasi yang pengguna tidak mau tahu ( perilaku enkapsulasi ), seperti dalam contoh Anda. Bayangkan, misalnya, bahwa selusin kelas menggunakan bidang publik Anda dan Anda perlu mengubah cara itu digunakan ... Anda harus melihat masing-masing kelas untuk mengubah cara mereka menggunakan bidang ... Tidak jadi "OOlysh".

Tapi, misalnya, Jika Anda memiliki bidang boolean yang disebut mati. Anda harus berpikir dua kali sebelum mendeklarasikan setDead dan isDead. Anda harus menulis pengakses yang dapat dibaca manusia , misalnya, kill () alih-alih setDead.

Namun, ada banyak kerangka kerja yang menganggap bahwa Anda mengikuti konvensi penamaan JavaBean (berbicara tentang Java di sini), oleh karena itu, dalam kasus tersebut, Anda harus mendeklarasikan semua getter dan setter mengikuti konvensi penamaan.

wleao
sumber
2
Sekarang "dapat dibaca manusia" akhirnya menjadi argumen yang bisa saya "grok." Semua argumen lain yang biasa saya dengar adalah semacam pemeriksaan di masa depan yang sama-sama mudah diatasi ketika kebutuhan muncul ...
Marjan Venema
13
Rasa jijik Anda untuk "pembuktian di masa depan" adalah umum, tetapi salah arah. Ya, Anda dapat mengatasi perubahan tersebut saat diperlukan, jika Anda adalah satu-satunya klien dari kode itu . Jika Anda tidak pernah mempublikasikan solusi Anda untuk klien eksternal, Anda dapat dengan mudah menimbulkan hutang teknis dan tidak ada yang perlu tahu. Tetapi proyek internal memiliki kebiasaan untuk menjadi publik ketika Anda tidak mengharapkannya, dan kemudian celakalah Anda.
Kilian Foth
2
Anda benar-benar meningkatkan poin yang fantastis. kill bukan set / get, itu aksi. Ini benar-benar menyembunyikan implementasi - ini adalah cara Anda mengkode OO. Properti, di sisi lain, sama bodohnya dengan setter / getter - lebih bodoh bahkan karena sintaksis yang mudah mendorong orang untuk hanya menambahkannya ke variabel ketika mereka bahkan tidak diperlukan (OO-Furbar menunggu untuk terjadi). Siapa yang akan berpikir untuk menggunakan kill () ketika mereka hanya bisa menambahkan tag "properti" ke bendera mati mereka?
Bill K
1
Pertanyaan ini ditandai dengan pertanyaan C ++. C ++ tidak mendukung properti, jadi mengapa Anda bahkan memposting ini?
Thomas Eding
1
@ThomasEding: C ++ agak unik karena memiliki operator penugasan yang dapat ditimpa. Sementara C ++ tidak menggunakan istilah "properti" dengan cara yang sama seperti bahasa seperti C # atau VB.NET, konsepnya masih ada. Dimungkinkan dalam C ++ untuk menggunakan operator overloading sehingga foo.bar = biz.baz+5akan memanggil fungsi bizuntuk mengevaluasi baz, kemudian menambahkan lima untuk itu dan memanggil fungsi aktif foountuk mengatur barke nilai yang dihasilkan. Kelebihan seperti itu tidak disebut "properti", tetapi mereka dapat melayani tujuan yang sama.
supercat
25

Ini bukan pendapat yang paling populer tetapi saya tidak melihat banyak perbedaan.

Setter dan getter adalah ide yang cukup buruk. Saya sudah memikirkannya dan jujur ​​saya tidak bisa menemukan perbedaan antara setter / pengambil properti dan variabel publik dalam praktik.

Dalam TEORI setter dan pengambil atau properti menambahkan tempat untuk mengambil beberapa tindakan tambahan ketika variabel diatur / didapat dan secara teori mereka mengisolasi kode Anda dari perubahan.

Pada kenyataannya saya jarang melihat setter dan getter digunakan untuk menambahkan aksi, dan ketika Anda ingin menambahkan aksi Anda ingin menambahkannya ke SEMUA setter atau getter dari suatu kelas (seperti logging) yang seharusnya membuat Anda berpikir bahwa harus ada menjadi solusi yang lebih baik.

Adapun untuk mengisolasi keputusan desain, jika Anda mengubah int untuk waktu yang lama, Anda masih harus mengubah setter Anda dan setidaknya memeriksa setiap baris yang mengaksesnya dengan tangan - tidak banyak isolasi di sana.

Kelas yang tidak dapat diubah harus dihindari secara default, jadi menambahkan setter harus menjadi pilihan terakhir. Ini dimitigasi dengan pola pembangun di mana nilai dapat diatur hingga objek berada dalam keadaan yang diinginkan maka kelas bisa menjadi tidak berubah dan setter Anda akan melempar pengecualian.

Sedangkan untuk getter - saya masih tidak bisa membuat banyak perbedaan antara pengambil dan variabel final publik. Masalahnya di sini adalah bahwa OO buruk dalam kedua kasus. Anda seharusnya tidak meminta nilai dari objek dan mengoperasikannya - Anda harus meminta objek untuk melakukan operasi untuk Anda.

Omong-omong, saya sama sekali tidak mengadvokasi variabel publik - saya katakan setter dan getter (dan bahkan properti) terlalu dekat dengan variabel publik yang sudah ada.

Masalah besarnya adalah orang-orang yang bukan pemrogram OO terlalu tergoda untuk menggunakan setter dan getter untuk membuat objek menjadi bola-properti (struktur) yang diedarkan dan dioperasikan, cukup berlawanan dengan cara kerja kode Berorientasi Objek.

Bill K
sumber
Satu hal yang baik terlepas dari jawaban lain di sini, tentang pengambil pengambil adalah: Saya dapat menambahkan beberapa validasi / konversi di dalamnya sebelum variabel pribadi yang sebenarnya mengasumsikan nilainya.
WinW
1
@Kiran benar, tetapi jika Anda mengaturnya di konstruktor Anda dapat memiliki validasi DAN Anda memiliki kelas yang tidak dapat diubah. Untuk setter saya tidak mengatakan variabel yang dapat ditulis publik itu baik, saya mengatakan bahwa seter pun buruk. Untuk getter saya mungkin mempertimbangkan variabel akhir publik sebagai alternatif yang layak.
Bill K
Dalam beberapa bahasa seperti C #, properti tidak kompatibel dengan biner dengan variabel publik, jadi jika Anda membuat variabel publik bagian dari API Anda, tetapi kemudian ingin menambahkan beberapa validasi, Anda akan merusak program klien Anda ketika Anda mengubahnya menjadi sebuah properti. Lebih baik menjadikannya milik umum sejak awal.
Robert Harvey
@RobertHarvey Itu masih berarti Anda mengekspos properti. Seluruh poin saya adalah bahwa mengekspos properti harus dihindari, dan bila perlu Anda harus mencoba membatasi mereka untuk getter dan membuat kelas Anda tidak berubah. Jika kelas Anda tidak dapat diubah, mengapa Anda membutuhkan kode dalam pengambil - dan karenanya Anda mungkin tidak memiliki pengambil. Jika Anda tidak mampu menulis kelas tanpa sifat yang bisa ditelusuri maka ya, setter selalu lebih baik daripada variabel publik yang dapat ditulis (dan ditembak di kaki selalu lebih baik daripada ditusuk di dalam usus).
Bill K
1
Keadaan yang dapat diubah dapat diterima, tetapi sekali lagi Anda harus meminta objek Anda untuk melakukan sesuatu untuk Anda, bukan melakukan sesuatu untuk objek Anda - oleh karena itu ketika Anda mengubah keadaan Anda, seorang penyetel bukanlah cara yang bagus untuk melakukannya, cobalah menulis metode bisnis dengan logika di dalamnya sebagai gantinya. Misalnya, "gerakkan (Up5) daripada setZLocation (getZLocation () + 5)".
Bill K
8

Pengguna getter dan setter masuk ke dalam prinsip enkapsulasi . Ini akan memungkinkan Anda untuk mengubah cara kerja di dalam kelas dan menjaga semuanya berfungsi.

Misalnya jika 3 objek lain memanggil foo.baruntuk mendapatkan nilai bar dan Anda memutuskan untuk mengubah nama bar sejauh Anda memiliki masalah di tangan Anda. Jika objek yang dipanggil foo.barAnda harus mengubah semua kelas yang memiliki ini. Jika setter / pengambil digunakan maka Anda tidak perlu mengubah apa pun. Kemungkinan lain adalah mengubah jenis variabel yang hanya menambahkan beberapa kode transformasi ke pengambil / penyetel dan Anda baik-baik saja.


sumber
1
+1 - variabel anggota adalah implementasi, pengambil dan penyetel adalah antarmuka. Hanya antarmuka yang harus publik. Juga, nama pengambil dan penyetel harus masuk akal dalam hal abstraksi, terlepas dari apakah ada variabel anggota sederhana yang mendasari mereka atau beberapa implementasi lainnya. Ada yang pengecualian - kasus di mana subsistem yang dibangun dari kelas erat-coupled adalah cara termudah - tapi itu hanya mendorong batas data bersembunyi naik tingkat pula, untuk kelas yang mewakili seluruh subsistem.
Steve314
Tidak bisakah saya cukup memperkenalkan pengambil dan / atau penyetel ketika saya perlu mengubah pekerjaan di dalam kelas? Ini adalah sesuatu yang mudah dilakukan dalam Delphi, tetapi mungkin tidak dalam bahasa lain?
Marjan Venema
@ marjan Tentu, Anda bisa memperkenalkan pengambil / penyetel kapan saja nanti. Masalahnya kemudian adalah bahwa Anda harus kembali dan mengubah SEMUA kode yang menggunakan bidang publik alih-alih pengambil / penyetel. Saya tidak yakin apakah saya orang yang bingung atau Anda. 0.o
Pete
3
Secara pribadi, saya pikir ini sedikit argumen palsu. Jika Anda mengubah arti variabel tetapi variabel memiliki pengambil dan penyetel, maka tidak masalah apa namanya. Itu bukan bagian yang terlihat dari antarmuka. Anda lebih mungkin ingin mengubah nama pengambil dan mengatur sendiri untuk menunjukkan kepada klien perubahan atau klarifikasi makna variabel yang dienkapsulasi. Dalam kasus terakhir ini, Anda tidak berada dalam posisi yang lebih baik dengan variabel publik. Ini cukup terlepas dari argumen bahwa variabel dengan getter dan setter lebih terbuka daripada dienkapsulasi.
CB Bailey
1
Operasi refactor sebenarnya gratis, jadi saya tidak benar-benar membeli yang ini. Plus itu Bentuk Sangat Buruk untuk menggunakan setter atau pengambil kecuali Anda benar-benar membutuhkannya - pada akhirnya Anda akan membuat orang yang tidak memahami konsep hanya secara otomatis menambahkan setter dan getter untuk anggota daripada ketika mereka dibutuhkan yang hanya meneteskan biji kejahatan di seluruh basis kode Anda.
Bill K
5

Menggunakan getter dan setter juga memungkinkan Anda untuk mengontrol konten apa yang disimpan dalam variabel tertentu. Jika konten perlu jenis atau nilai tertentu, bagian dari kode penyetel Anda dapat memastikan bahwa nilai yang baru memenuhi persyaratan ini. Jika variabel bersifat publik, Anda tidak dapat memastikan persyaratan ini dipenuhi.

Pendekatan ini juga membuat kode Anda lebih mudah beradaptasi dan dikelola. Jauh lebih mudah untuk membuat perubahan pada arsitektur kelas jika Anda memiliki fungsi di tempat yang membuat arsitektur itu tersembunyi dari semua kelas lain atau fungsi yang memanfaatkan kelas itu. Perubahan nama variabel yang telah disebutkan hanyalah satu dari banyak perubahan yang lebih mudah dilakukan jika Anda memiliki fungsi seperti getter dan setter. Gagasan keseluruhan adalah untuk menjaga privasi sebanyak mungkin, terutama variabel kelas Anda.

Kenneth
sumber
Terima kasih banyak! Saya sedang memikirkan kasus yang sama yang Anda sebutkan. Jika saya ingin variabel menjadi konten dalam [0,1] Saya harus memeriksa nilai yang masuk dalam setternya :)
Oni
Ketika Anda adalah satu-satunya pengembang yang mengerjakan suatu proyek, mudah untuk berpikir bahwa hal-hal seperti ini tidak berguna tetapi ketika Anda mendapati diri Anda bekerja dengan sebuah tim, Anda akan menemukan bahwa dengan kebiasaan teknik seperti ini akan sangat berguna dan membantu Anda hindari banyak bug di jalan.
Kenneth
Jika Anda menggunakan C #, gunakan properti, Anda dapat menggunakan properti untuk menetapkan instance pribadi jika diperlukan berdasarkan pada beberapa logika di bagian setter properti.
developerdoug
2

Anda mengatakan "Satu-satunya kasus saya pikir Anda harus menggunakan getter dan setter adalah jika Anda perlu melakukan beberapa operasi selain set atau get."

Anda harus menggunakan getter dan setter jika pada suatu saat nanti Anda mungkin perlu melakukan beberapa operasi selain set dan get dan Anda tidak ingin mengubah ribuan baris kode sumber ketika itu terjadi.

Anda harus menggunakan getter dan setter jika Anda tidak ingin seseorang mengambil alamat variabel dan menyebarkannya, dengan konsekuensi yang merusak jika variabel itu kemudian dapat diubah tanpa kode sumber menyebutkannya, atau bahkan setelah objek berhenti ada lagi .

gnasher729
sumber
Paragraf terakhir Anda memberikan poin yang sangat bagus yang memotong kedua cara: mengharuskan penggunaan getter dan setter akan mencegah kode lain mengambil alamat sesuatu. Dalam kasus di mana akan ada manfaat yang signifikan dan beberapa kerugian untuk memiliki kode luar mengambil alamat hal-hal, pembatasan seperti itu bisa menjadi hal yang buruk. jika ada sedikit manfaat dan kerugian besar untuk membiarkan perilaku seperti itu, membatasi itu bisa menjadi hal yang baik.
supercat
1

Pertama mari kita perjelas paradigma.

  • Struktur Data -> tata letak memori yang dapat dilalui dan dimanipulasi oleh fungsi yang sesuai dengan pengetahuan.
  • Objek -> modul mandiri yang menyembunyikan implementasinya dan menyediakan antarmuka yang dapat dikomunikasikan.

Di mana pengambil / penyetel berguna?

Apakah getter / setter berguna dalam Struktur Data? Tidak.

Struktur Data adalah spesifikasi tata letak memori yang umum dan dimanipulasi oleh sekumpulan fungsi.

Secara umum, setiap fungsi baru yang lama dapat muncul dan memanipulasi struktur data, jika ia melakukannya sedemikian rupa sehingga fungsi lainnya masih dapat memahaminya, maka fungsi tersebut bergabung dengan keluarga. Kalau tidak, ini adalah fungsi jahat dan sumber bug.

Jangan salah paham, mungkin ada beberapa keluarga fungsi yang berperang karena struktur data dengan snitch, turn-coats, dan agen ganda di mana-mana. Tidak apa-apa ketika mereka masing-masing memiliki struktur data sendiri untuk dimainkan, tetapi ketika mereka membagikannya ... bayangkan saja beberapa keluarga kriminal yang tidak setuju dengan politik, itu bisa menjadi kekacauan yang sangat cepat.

Mengingat kekacauan yang dapat dicapai fungsi keluarga, apakah ada cara untuk menyandikan struktur data sehingga fungsi jahat tidak mengacaukan semuanya? Ya, mereka disebut Objects.

Apakah getter / setter berguna di Objek? Tidak.

Inti dari membungkus struktur data di suatu objek adalah untuk memastikan bahwa tidak ada fungsi jahat yang bisa ada. Jika fungsi ingin bergabung dengan keluarga, itu harus diperiksa terlebih dahulu dan kemudian menjadi bagian dari objek.

Titik / tujuan pengambil dan penyetel adalah untuk memungkinkan fungsi di luar objek untuk mengubah tata letak memori objek secara langsung. Kedengarannya seperti pintu terbuka untuk mengizinkan penyamun ...

Kasus Tepi

Ada dua situasi ketika seorang pengambil / pembuat keputusan masuk akal.

  • Sebagian dari struktur data dalam objek dikelola oleh objek, tetapi tidak dikendalikan oleh objek.
  • Antarmuka yang menggambarkan abstraksi tingkat tinggi dari struktur data di mana beberapa elemen diharapkan tidak dapat mengendalikan objek pelaksana.

Kontainer dan antarmuka wadah adalah contoh sempurna dari kedua situasi ini. Kontainer mengelola struktur data (daftar terkait, peta, pohon) secara internal tetapi dengan mudah mengendalikan elemen tertentu untuk semuanya. Antarmuka mengabstraksi hal ini dan mengabaikan implementasi sepenuhnya dan hanya menggambarkan harapan.

Sayangnya banyak implementasi yang salah dan mendefinisikan antarmuka objek semacam ini untuk memberikan akses langsung ke objek yang sebenarnya. Sesuatu seperti:

interface Container<T>
{
    typedef ...T... TRef; //<somehow make TRef to be a reference or pointer to the memory location of T
    TRef item(int index); 
}

Ini rusak Implementasi Container harus secara eksplisit menyerahkan kontrol internal mereka kepada siapa pun yang menggunakannya. Saya belum melihat bahasa dengan nilai yang bisa berubah-ubah di mana ini baik-baik saja (bahasa dengan semantik bernilai tidak berubah secara definisi baik dari perspektif korupsi data, tetapi tidak harus dari perspektif mata-mata data).

Anda dapat meningkatkan / memperbaiki getter / setter dengan hanya menggunakan copy-semantik, atau dengan menggunakan proxy:

interface Proxy<T>
{
     operator T(); //<returns a copy
     ... operator ->(); //<permits a function call to be forwarded to an element
     Proxy<T> operator=(T); //< permits the specific element to be replaced/assigned by another T.
}

interface Container<T>
{
     Proxy<T> item(int index);
     T item(int index); //<When T is a copy of the original value.
     void item(int index, T new_value); //<where new_value is used to replace the old value
}

Bisa dibilang fungsi jahat masih bisa memainkan kekacauan di sini (dengan upaya yang cukup banyak hal yang mungkin), tetapi copy-semantik dan / atau proksi mengurangi kemungkinan sejumlah kesalahan.

  • meluap
  • underflow
  • interaksi dengan sub elemen adalah tipe-checked / type-checkable (dalam bahasa tipe hilang ini adalah anugerah)
  • Elemen aktual mungkin atau mungkin bukan memori.

Setter Pribadi / Setter

Ini adalah benteng getter dan setter terakhir yang bekerja pada tipe ini secara langsung. Sebenarnya saya bahkan tidak akan memanggil getter dan setter ini selain accessor dan manipulator.

Dalam konteks ini kadang-kadang memanipulasi bagian tertentu dari struktur data selalu / hampir selalu / umumnya membutuhkan pembukuan khusus untuk terjadi. Katakanlah ketika Anda memperbarui root dari pohon cache yang perlu disingkirkan harus dibersihkan, atau ketika Anda mengakses elemen data eksternal, kunci perlu diperoleh / dirilis. Dalam kasus ini masuk akal untuk menerapkan prinsip KERING dan membagi tindakan tersebut bersama-sama.

Dalam konteks pribadi, masih mungkin bagi fungsi-fungsi lain dalam keluarga untuk mengesampingkan 'getter dan setter' ini dan memanipulasi struktur data. Karena itu mengapa saya menganggap mereka lebih sebagai penentu dan manipulator. Anda dapat mengakses data secara langsung, atau mengandalkan anggota keluarga lain untuk mendapatkan bagian itu dengan benar.

Getters / Setters yang dilindungi

Dalam konteks yang dilindungi, tidak jauh berbeda dengan konteks publik. Fungsi asing yang mungkin ingin akses ke struktur data. Jadi tidak, jika mereka ada mereka beroperasi seperti pengambil / setter publik.

Kain0_0
sumber
0

Dari pengalaman C ++, Setter / getter sangat membantu untuk 2 skenario:

  1. jika Anda harus melakukan pemeriksaan kewarasan sebelum memberikan nilai kepada anggota seperti string atau array.
  2. ini bisa membantu ketika fungsi yang berlebihan diperlukan seperti nilai int ke tugas bool ketika -1 (atau nilai negatif) salah. dan sebaliknya untuk pengambil.

Terlepas dari itu, keamanan adalah titik yang valid tetapi kepentingannya terbatas pada sangat sedikit aplikasi pengembangan seperti modul terkait akses masuk atau db. Mengapa repot-repot membuat kode tambahan ketika hanya sedikit orang yang menggunakan modul Anda?

Ajay nandoriya
sumber