Mengapa menggunakan getter dan setter / accessor?


1544

Apa keuntungan menggunakan getter dan setter - yang hanya mendapatkan dan mengatur - daripada hanya menggunakan bidang publik untuk variabel-variabel itu?

Jika getter dan setter pernah melakukan lebih dari sekedar get / set sederhana, saya bisa mengetahui ini dengan sangat cepat, tapi saya tidak 100% jelas tentang caranya:

public String foo;

lebih buruk dari:

private String foo;
public void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }

Sedangkan yang pertama mengambil kode boilerplate jauh lebih sedikit.


6
@Dean J: Gandakan dengan banyak pertanyaan lain: stackoverflow.com/search?q=getters+setters
Asaph

8
Tentu saja, keduanya sama-sama buruk ketika objek tidak perlu diubah properti. Saya lebih suka menjadikan semuanya pribadi, dan kemudian menambahkan getter jika berguna, dan setter jika diperlukan.
Tordek

26
Google "accessors are evil"
OMG Ponies

34
"Aksesor jahat" jika Anda menulis kode fungsional atau objek yang tidak dapat diubah. Jika Anda menulis objek yang bisa berubah, maka objek tersebut sangat penting.
Christian Hayter

Jawaban:


982

Sebenarnya ada banyak alasan bagus untuk mempertimbangkan menggunakan pengakses daripada langsung mengekspos bidang kelas - di luar argumen enkapsulasi dan membuat perubahan di masa depan lebih mudah.

Berikut adalah beberapa alasan yang saya ketahui:

  • Enkapsulasi perilaku yang terkait dengan mendapatkan atau mengatur properti - ini memungkinkan fungsionalitas tambahan (seperti validasi) ditambahkan lebih mudah nanti.
  • Menyembunyikan representasi internal properti sambil mengekspos properti menggunakan representasi alternatif.
  • Mengisolasi antarmuka publik Anda dari perubahan - memungkinkan antarmuka publik tetap konstan sementara implementasinya berubah tanpa mempengaruhi konsumen yang ada.
  • Mengontrol semantik dan manajemen memori (pembuangan) semantik properti - khususnya penting dalam lingkungan memori yang tidak dikelola (seperti C ++ atau Objective-C).
  • Memberikan titik intersepsi debugging ketika properti berubah saat runtime - debugging kapan dan di mana properti diubah ke nilai tertentu bisa sangat sulit tanpa ini dalam beberapa bahasa.
  • Interoperabilitas yang ditingkatkan dengan perpustakaan yang dirancang untuk beroperasi terhadap pengambil properti / setter - Mocking, Serialisasi, dan WPF muncul di pikiran.
  • Mengizinkan pewaris mengubah semantik bagaimana perilaku properti dan diekspos dengan mengabaikan metode pengambil / penyetel.
  • Mengizinkan pengambil / penyetel untuk diedarkan sebagai ekspresi lambda daripada nilai.
  • Getters dan setters dapat memungkinkan tingkat akses yang berbeda - misalnya get mungkin publik, tetapi set bisa dilindungi.

60
+1. Hanya untuk menambahkan: Mengizinkan pemuatan malas. Mengizinkan salinan di tulis.
NewbiZ

6
@ bjarkef: Saya percaya publicmetode accessor menyebabkan duplikasi kode, dan mengekspos informasi. Aksesor publik tidak diperlukan untuk menyusun (serialisasi). Dalam beberapa bahasa (Java), publicdapatkan aksesor tidak dapat memiliki tipe pengembalian diubah tanpa melanggar kelas dependen. Selain itu, saya percaya mereka bertentangan dengan prinsip-prinsip OO. Lihat juga: stackoverflow.com/a/1462424/59087
Dave Jarvis

15
@ Sbi: Satu hal yang dibeli dengan menggunakan setter properti, bahkan dalam kerangka kerja yang dirancang dengan baik, adalah kemampuan untuk memiliki objek memberitahukan yang lain ketika properti berubah.
supercat

22
@supercat: Namun, saya umumnya tidak mempromosikan data publik. Pemberitahuan objek lain juga bisa dilakukan dari metode nyata memberikan abstraksi yang signifikan pada wadah data belaka dengan (lebih atau kurang) bidang data publik. Alih-alih plane.turnTo(dir); plane.setSpeed(spd); plane.setTargetAltitude(alt); plane.getBreaks().release();saya ingin mengatakan plane.takeOff(alt). Apa bidang data dalam yang perlu diubah untuk memasukkan pesawat ke takingOffmode tidak menjadi perhatian saya. Dan apa objek lain ( breaks) metode memberitahu saya juga tidak ingin tahu.
sbi

8
@ BenLee: Saya benar-benar tidak tahu bagaimana mengatakannya. "Pengakses" hanyalah sinonim untuk "getter / setter", dan dalam dua komentar pertama saya, saya menjelaskan mengapa itu adalah penyalahgunaan istilah "OOP". Jika Anda perlu menjangkau dan memanipulasi negara secara langsung, itu bukan objek dalam arti OOP dari istilah itu.
sbi

480

Karena 2 minggu (bulan, tahun) dari sekarang ketika Anda menyadari bahwa setter Anda perlu melakukan lebih dari sekadar menetapkan nilai, Anda juga akan menyadari bahwa properti tersebut telah digunakan secara langsung di 238 kelas lainnya :-)


84
Saya duduk dan menatap aplikasi garis 500k yang tidak pernah dibutuhkan. Yang mengatakan, jika diperlukan sekali, itu akan mulai menyebabkan mimpi buruk pemeliharaan. Cukup bagus untuk tanda centang untuk saya.
Dean J

20
Saya hanya bisa iri dengan Anda dan aplikasi Anda :-) Itu mengatakan, itu sangat tergantung pada tumpukan perangkat lunak Anda juga. Delphi, misalnya (dan C # - saya pikir?) Memungkinkan Anda untuk mendefinisikan properti sebagai warga negara kelas 1 di mana mereka dapat membaca / menulis bidang secara langsung pada awalnya tetapi - jika Anda membutuhkannya - melakukannya melalui metode pengambil / penyetel juga. Lebih nyaman. Java, sayangnya, tidak - belum lagi standar Jawa yang memaksa Anda untuk menggunakan getter / setter juga.
ChssPly76

14
Walaupun ini memang merupakan alasan yang baik untuk menggunakan pengakses, banyak lingkungan pemrograman dan editor sekarang menawarkan dukungan untuk refactoring (baik dalam IDE atau sebagai add-in gratis) yang agak mengurangi dampak masalah.
LBushkin

62
Kedengarannya seperti optimasi pra-dewasa
cmcginty

41
Pertanyaan yang perlu Anda tanyakan ketika Anda bertanya-tanya apakah menerapkan getter dan setter adalah: Mengapa pengguna kelas perlu mengakses jeroan kelas sama sekali? Tidak masalah apakah mereka melakukannya secara langsung atau dilindungi oleh lapisan pseudo tipis - jika pengguna perlu mengakses detail implementasi, maka itu pertanda bahwa kelas tidak menawarkan cukup banyak abstraksi. Lihat juga komentar ini .
sbi

358

Bidang publik tidak lebih buruk daripada pasangan pengambil / penyetel yang tidak melakukan apa pun kecuali mengembalikan bidang dan menugaskannya. Pertama, jelas bahwa (dalam kebanyakan bahasa) tidak ada perbedaan fungsional. Perbedaan apa pun harus dalam faktor lain, seperti rawatan atau keterbacaan.

Keuntungan yang sering disebutkan dari pasangan pengambil / penyetel, tidak. Ada klaim ini bahwa Anda dapat mengubah implementasi dan klien Anda tidak perlu dikompilasi ulang. Seharusnya, setter membiarkan Anda menambahkan fungsionalitas seperti validasi di kemudian hari dan klien Anda bahkan tidak perlu mengetahuinya. Namun, menambahkan validasi ke setter adalah perubahan pada prasyaratnya, pelanggaran kontrak sebelumnya , yang, secara sederhana, "Anda dapat memasukkan apa pun di sini, dan Anda bisa mendapatkan hal yang sama nanti dari pengambil".

Jadi, sekarang setelah Anda melanggar kontrak, mengubah setiap file dalam basis kode adalah sesuatu yang harus Anda lakukan, bukan menghindari. Jika Anda menghindarinya, Anda membuat asumsi bahwa semua kode menganggap kontrak untuk metode itu berbeda.

Jika itu seharusnya bukan kontrak, maka antarmuka memungkinkan klien untuk meletakkan objek dalam keadaan tidak valid. Itu kebalikan dari enkapsulasi. Jika bidang itu tidak dapat benar-benar diatur ke apa pun dari awal, mengapa tidak ada validasi di sana sejak awal?

Argumen yang sama ini berlaku untuk keuntungan dugaan lain dari pasangan pengambil / penyetel pass-through ini: jika nanti Anda memutuskan untuk mengubah nilai yang ditetapkan, Anda melanggar kontrak. Jika Anda menimpa fungsionalitas default di kelas turunan, dengan cara di luar beberapa modifikasi yang tidak berbahaya (seperti logging atau perilaku tidak dapat diamati lainnya), Anda melanggar kontrak dari kelas dasar. Itu adalah pelanggaran terhadap Prinsip Pengganti Liskov, yang dipandang sebagai salah satu prinsip OO.

Jika suatu kelas memiliki para pengambil getah dan setter bodoh ini untuk setiap bidang, maka itu adalah kelas yang tidak memiliki invarian apa pun, tidak ada kontrak . Apakah itu benar-benar desain berorientasi objek? Jika semua kelas memiliki getter dan setter, itu hanya pemegang data bodoh, dan pemegang data bodoh harus terlihat seperti pemegang data bodoh:

class Foo {
public:
    int DaysLeft;
    int ContestantNumber;
};

Menambahkan pasangan pengambil / penyetel pass-through ke kelas semacam itu tidak menambah nilai. Kelas lain harus menyediakan operasi yang bermakna, bukan hanya operasi yang sudah disediakan bidang. Itulah cara Anda dapat mendefinisikan dan mempertahankan invarian yang bermanfaat.

Klien : "Apa yang bisa saya lakukan dengan objek kelas ini?"
Desainer : "Anda dapat membaca dan menulis beberapa variabel."
Klien : "Oh ... keren, kurasa?"

Ada alasan untuk menggunakan getter dan setter, tetapi jika alasan itu tidak ada, membuat pasangan pengambil / penyetel atas nama dewa enkapsulasi palsu bukanlah hal yang baik. Alasan yang sah untuk membuat getter atau setter mencakup hal-hal yang sering disebutkan sebagai potensi perubahan yang dapat Anda buat nanti, seperti validasi atau representasi internal yang berbeda. Atau mungkin nilainya harus dibaca oleh klien tetapi tidak dapat ditulis (misalnya, membaca ukuran kamus), jadi pengambil sederhana adalah pilihan yang bagus. Tetapi alasan-alasan itu harus ada ketika Anda membuat pilihan, dan bukan hanya sebagai potensi yang Anda inginkan nanti. Ini adalah contoh dari YAGNI ( Kamu Tidak Akan Membutuhkannya ).


13
Jawaban bagus (+1). Satu-satunya kritik saya adalah bahwa saya perlu beberapa kali membaca untuk mengetahui bagaimana "validasi" dalam paragraf terakhir berbeda dari "validasi" dalam beberapa yang pertama (yang Anda buang dalam kasus terakhir tetapi dipromosikan di yang sebelumnya); menyesuaikan kata-kata dapat membantu dalam hal itu.
Lightness Races di Orbit

14
Ini adalah jawaban yang bagus tetapi sayangnya era saat ini telah lupa untuk apa "menyembunyikan informasi" atau untuk apa. Mereka tidak pernah membaca tentang kekekalan dan dalam pencarian mereka untuk yang paling gesit, tidak pernah menggambar diagram transisi negara yang mendefinisikan apa status hukum suatu objek dan dengan demikian apa yang tidak.
Darrell Teague

2
Pengamatan utama adalah bahwa getter jauh lebih berguna daripada setter. Seorang pengambil dapat mengembalikan nilai yang dihitung, atau nilai yang dihitung dalam cache. Yang bisa dilakukan setter hanyalah validasi dan kemudian ganti privatebidang. Jika suatu kelas tidak memiliki setter, mudah untuk membuatnya tidak berubah.
Raedwald

7
Eh, saya kira Anda tidak tahu apa itu kelas invarian. Jika itu adalah struktur non-sepele, itu cukup banyak berarti memiliki invarian untuk ditegakkan dan orang tidak dapat mendesainnya tanpa memikirkannya. "Ada bug, satu anggota salah diperbarui." juga cukup banyak berbunyi seperti "Pembaruan anggota melanggar invarian kelas". Kelas yang dirancang dengan baik tidak memungkinkan kode klien untuk melanggar invariannya.
R. Martinho Fernandes

5
+1 untuk 'pemegang data bodoh harus terlihat seperti pemegang data bodoh' Sayangnya, semua orang tampaknya merancang segala sesuatu di sekitar pemegang data bodoh hari ini, dan banyak kerangka kerja bahkan membutuhkannya ...
Joeri Hendrickx

93

Banyak orang berbicara tentang keuntungan dari getter dan setter tetapi saya ingin bermain sebagai penasihat iblis. Saat ini saya sedang men-debug program yang sangat besar di mana para programmer memutuskan untuk membuat semuanya getter dan setter. Itu mungkin tampak bagus, tapi itu mimpi buruk rekayasa terbalik.

Katakanlah Anda sedang mencari melalui ratusan baris kode dan Anda menemukan ini:

person.name = "Joe";

Ini hanya sepotong kode yang indah sampai Anda menyadari bahwa ini adalah setter. Sekarang, Anda mengikuti setter itu dan menemukan bahwa ia juga menetapkan person.firstName, person.lastName, person.isHuman, person.hasReallyCommonFirstName, dan memanggil person.update (), yang mengirimkan permintaan ke database, dll. Oh, itu di mana kebocoran memori Anda terjadi.

Memahami sepotong kode lokal pada pandangan pertama adalah sifat penting dari keterbacaan yang baik yang membuat getter dan setter cenderung rusak. Itulah sebabnya saya mencoba menghindarinya ketika saya bisa, dan meminimalkan apa yang mereka lakukan ketika saya menggunakannya.


8
Iya. Saat ini refactoring basis kode besar, dan ini telah menjadi mimpi buruk. Para getter dan setter melakukan terlalu banyak, termasuk memanggil getter dan setter yang sangat sibuk lainnya yang akhirnya mengurangi keterbacaan menjadi tidak ada artinya. Validasi adalah alasan bagus untuk menggunakan pengakses, tetapi menggunakannya untuk melakukan lebih dari itu tampaknya menghilangkan manfaat potensial.
Fadecomic

31
Ini adalah argumen terhadap gula sintaksis, bukan menentang setter pada umumnya.
Phil

6
Benar dan benar tetapi lebih jauh lagi, ini menunjukkan bagaimana mengapa setor properti individu membuka pintu bagi negara yang tidak valid sama sekali tanpa obat untuk memastikan integritas dari objek tertentu yang memungkinkan abstraksi bocor.
Darrell Teague

"Ini hanya sepotong kode yang indah sampai Anda menyadari bahwa itu adalah setter" - hei, apakah posting asli tentang Jawa atau tentang C #? :)
Honza Zidek

Saya sepenuhnya setuju tentang keterbacaan. Benar-benar eksplisit apa yang terjadi ketika person.name = "Joe";dipanggil. Tidak ada cacat di jalan informasi, sintaks menyoroti menunjukkan itu bidang dan refactoring lebih sederhana (tidak perlu merefleksikan dua metode dan bidang). Kedua, semua siklus otak yang terbuang itu berpikir "getIsValid ()" atau seharusnya "isValid ()" dll. Dapat dilupakan. Saya tidak dapat memikirkan satu kali pun saya telah diselamatkan dari bug oleh pengambil / penyetel.
Will

53

Ada banyak alasan. Favorit saya adalah ketika Anda perlu mengubah perilaku atau mengatur apa yang dapat Anda atur pada variabel. Misalnya, katakan Anda memiliki metode setSpeed ​​(kecepatan int). Tetapi Anda ingin bahwa Anda hanya dapat mengatur kecepatan maksimum 100. Anda akan melakukan sesuatu seperti:

public void setSpeed(int speed) {
  if ( speed > 100 ) {
    this.speed = 100;
  } else {
    this.speed = speed;
  }
}

Sekarang bagaimana jika DI MANA SAJA dalam kode Anda Anda menggunakan bidang publik dan kemudian Anda menyadari bahwa Anda membutuhkan persyaratan di atas? Bersenang-senang memburu setiap penggunaan bidang publik alih-alih hanya memodifikasi setter Anda.

2 sen saya :)


62
Memburu setiap penggunaan bidang publik seharusnya tidak terlalu sulit. Jadikan pribadi dan biarkan kompiler menemukannya.
Nathan Fellman

12
itu benar tentu saja, tetapi mengapa membuatnya lebih sulit dari yang seharusnya. Pendekatan get / set masih merupakan jawaban yang lebih baik.
Hardryv

28
@Nathan: Menemukan penggunaan lain bukanlah masalah. Mengubah semuanya adalah.
Graeme Perrow

22
@GraemePerrow harus mengubah semuanya adalah keuntungan, bukan masalah :( Bagaimana jika Anda memiliki kode yang mengasumsikan kecepatan bisa lebih tinggi dari 100 (karena, Anda tahu, sebelum Anda melanggar kontrak, itu bisa!) ( while(speed < 200) { do_something(); accelerate(); })
R. Martinho Fernandes

29
Ini adalah contoh yang SANGAT buruk! Seseorang harus menelepon: myCar.setSpeed(157);dan setelah beberapa baris speed = myCar.getSpeed();Dan sekarang ... Saya harap Anda senang men-debug sambil mencoba memahami mengapa speed==100harus157
Piotr Aleksander Chmielowski

53

Dalam dunia yang murni berorientasi objek, pengambil dan penentu adalah anti-pola yang mengerikan . Baca artikel ini: Getters / Setters. Jahat. Periode . Singkatnya, mereka mendorong programmer untuk berpikir tentang objek sebagai struktur data, dan jenis pemikiran ini murni prosedural (seperti dalam COBOL atau C). Dalam bahasa berorientasi objek tidak ada struktur data, tetapi hanya objek yang mengekspos perilaku (bukan atribut / properti!)

Anda dapat menemukan lebih banyak tentang mereka di Bagian 3.5 dari Objek Elegan (buku saya tentang pemrograman berorientasi objek).


7
Getters dan setter menyarankan model domain anemia.
Raedwald

7
Sudut pandang yang menarik. Tetapi dalam sebagian besar konteks pemrograman yang kita butuhkan adalah struktur data. Ambil contoh "Anjing" artikel yang ditautkan. Ya, Anda tidak dapat mengubah berat anjing dunia nyata dengan menetapkan atribut ... tetapi a new Dog()bukan anjing. Ini adalah objek yang menyimpan informasi tentang seekor anjing. Dan untuk penggunaan itu, adalah wajar untuk dapat memperbaiki berat yang salah dicatat.
Stephen C

3
Yah, saya katakan kepada Anda bahwa program yang paling berguna tidak perlu memodelkan / mensimulasikan objek dunia nyata. IMO, ini bukan tentang bahasa pemrograman sama sekali. Ini tentang apa yang kami tulis untuk program.
Stephen C

3
Dunia nyata atau tidak, Yegor sepenuhnya benar. Jika apa yang Anda miliki benar-benar "Struct" dan Anda tidak perlu menulis kode apa pun yang merujuknya dengan nama, masukkan ke dalam hashtable atau struktur data lainnya. Jika Anda memang perlu menulis kode untuk itu maka letakkan sebagai anggota kelas dan masukkan kode yang memanipulasi variabel itu di kelas yang sama dan hilangkan setter & pengambil. PS. walaupun saya sebagian besar berbagi sudut pandang yegor, saya menjadi percaya bahwa kacang beranotasi tanpa kode adalah struktur data yang agak berguna - juga getter terkadang diperlukan, setter seharusnya tidak pernah ada.
Bill K

1
Saya terbawa suasana - seluruh jawaban ini, meskipun benar dan relevan, tidak menjawab pertanyaan secara langsung. Mungkin seharusnya mengatakan "Baik setter / pengambil dan variabel publik salah" ... Untuk lebih spesifik, setter dan variabel publik yang dapat ditulis tidak boleh digunakan sedangkan getter hampir sama dengan variabel akhir publik dan kadang-kadang diperlukan kejahatan tetapi tidak ada yang lebih baik dari yang lain.
Bill K

38

Satu keuntungan dari pengakses dan mutator adalah Anda dapat melakukan validasi.

Misalnya, jika foobersifat publik, saya dapat dengan mudah mengaturnya nulldan kemudian orang lain dapat mencoba memanggil metode pada objek. Tapi itu sudah tidak ada lagi! Dengan setFoometode, saya bisa memastikan bahwa footidak pernah diatur null.

Accessor dan mutators juga memungkinkan untuk enkapsulasi - jika Anda tidak seharusnya melihat nilai begitu ditetapkan (mungkin itu diatur dalam konstruktor dan kemudian digunakan oleh metode, tetapi tidak pernah seharusnya diubah), itu tidak akan pernah dilihat oleh siapa pun. Tetapi jika Anda dapat mengizinkan kelas lain untuk melihat atau mengubahnya, Anda dapat memberikan accessor dan / atau mutator yang tepat.


28

Tergantung pada bahasa Anda. Anda menandai ini "berorientasi objek" daripada "Java", jadi saya ingin menunjukkan bahwa jawaban ChssPly76 bergantung pada bahasa. Dalam Python, misalnya, tidak ada alasan untuk menggunakan getter dan setter. Jika Anda perlu mengubah perilaku, Anda dapat menggunakan properti, yang membungkus pengambil dan penyetel di sekitar akses atribut dasar. Sesuatu seperti ini:

class Simple(object):
   def _get_value(self):
       return self._value -1

   def _set_value(self, new_value):
       self._value = new_value + 1

   def _del_value(self):
       self.old_values.append(self._value)
       del self._value

   value = property(_get_value, _set_value, _del_value)

2
Ya, saya sudah mengatakan banyak dalam komentar di bawah jawaban saya. Java bukan satu-satunya bahasa yang menggunakan getter / setter sebagai kruk seperti halnya Python bukan satu-satunya bahasa yang dapat mendefinisikan properti. Poin utama, bagaimanapun, masih tetap - "properti" bukan "bidang publik" yang sama.
ChssPly76

1
@ jcd - tidak sama sekali. Anda mendefinisikan "antarmuka" Anda (API publik akan menjadi istilah yang lebih baik di sini) dengan mengekspos bidang publik Anda. Setelah itu selesai, tidak ada jalan untuk kembali. Properti BUKAN bidang karena memberi Anda mekanisme untuk mencegat upaya untuk mengakses bidang (dengan merutekannya ke metode jika itu ditentukan); namun demikian, tidak lebih dari sintaksis daripada metode pengambil / penyetel. Ini sangat mudah tetapi tidak mengubah paradigma yang mendasarinya - mengekspos bidang tanpa kontrol atas akses ke mereka melanggar prinsip enkapsulasi.
ChssPly76

14
@ ChssPly76 — saya tidak setuju. Saya hanya memiliki kontrol sebanyak seolah-olah mereka adalah properti, karena saya dapat membuat mereka properti kapan pun saya perlu. Tidak ada perbedaan antara properti yang menggunakan getter dan setter boilerplate, dan atribut mentah, kecuali bahwa atribut mentah lebih cepat, karena menggunakan bahasa yang mendasarinya, daripada memanggil metode. Secara fungsional, mereka identik. Satu-satunya cara enkapsulasi bisa dilanggar adalah jika Anda berpikir tanda kurung ( obj.set_attr('foo')) secara inheren lebih tinggi dari tanda sama dengan ( obj.attr = 'foo'). Akses publik adalah akses publik.
jcdyer

1
@ jcdyer lebih banyak mengontrol ya, tetapi tidak sebanyak keterbacaan, yang lain sering salah berasumsi bahwa obj.attr = 'foo'hanya menetapkan variabel tanpa hal lain terjadi
Timo Huovinen

9
@TimoHuovinen Apa bedanya dengan pengguna di Java dengan anggapan obj.setAttr('foo')"set variabel saja tanpa ada yang terjadi"? Jika ini metode publik, maka itu metode publik. Jika Anda menggunakannya untuk mencapai beberapa efek samping, dan ini bersifat publik, maka Anda sebaiknya dapat mengandalkan segala sesuatu yang bekerja seolah-olah hanya efek samping yang dimaksud yang terjadi (dengan semua detail implementasi lainnya dan efek samping lainnya, penggunaan sumber daya, apa pun , disembunyikan dari kekhawatiran pengguna). Ini sama sekali tidak berbeda dengan Python. Sintaksis Python untuk mencapai efeknya lebih sederhana.
ely

25

Yah saya hanya ingin menambahkan bahwa meskipun kadang-kadang mereka diperlukan untuk enkapsulasi dan keamanan variabel Anda / objek, jika kita ingin kode Program Berorientasi Objek nyata, maka kita perlu BERHENTI MENGATASI AKSESORI , karena kadang-kadang kita sangat bergantung pada mereka ketika tidak benar-benar diperlukan dan itu membuat hampir sama seperti jika kita meletakkan variabel publik.


24

Terima kasih, itu benar-benar menjelaskan pemikiran saya. Sekarang inilah (hampir) 10 (hampir) alasan bagus untuk TIDAK menggunakan getter dan setter:

  1. Ketika Anda menyadari bahwa Anda perlu melakukan lebih dari sekadar menetapkan dan mendapatkan nilai, Anda bisa menjadikan bidang tersebut pribadi, yang akan langsung memberi tahu Anda di mana Anda langsung mengaksesnya.
  2. Validasi apa pun yang Anda lakukan di sana hanya bisa bebas konteks, yang validasi jarang dilakukan.
  3. Anda dapat mengubah nilai yang ditetapkan - ini adalah mimpi buruk absolut ketika penelepon memberikan Anda nilai yang mereka [syok kejutan] ingin Anda simpan SEBAGAIMANA ADANYA.
  4. Anda dapat menyembunyikan representasi internal - fantastis, sehingga Anda memastikan bahwa semua operasi ini simetris bukan?
  5. Anda telah mengisolasi antarmuka publik Anda dari perubahan di bawah lembaran - jika Anda mendesain antarmuka dan tidak yakin apakah akses langsung ke sesuatu OK, maka Anda harus terus merancang.
  6. Beberapa perpustakaan mengharapkan ini, tetapi tidak banyak - refleksi, serialisasi, benda tiruan semuanya bekerja dengan baik dengan bidang publik.
  7. Mewarisi kelas ini, Anda dapat mengesampingkan fungsi default - dengan kata lain Anda benar-benar dapat membingungkan penelepon dengan tidak hanya menyembunyikan implementasi tetapi membuatnya tidak konsisten.

Tiga yang terakhir saya baru saja pergi (N / A atau D / C) ...


11
Saya pikir argumen krusial adalah bahwa, "jika Anda mendesain antarmuka dan tidak yakin apakah akses langsung ke sesuatu itu OK, maka Anda harus terus mendesain." Itu adalah masalah yang paling penting dengan getter / setter: Mereka mereduksi kelas menjadi sekadar wadah (kurang lebih) bidang publik. Di nyata OOP, bagaimanapun, sebuah benda lebih dari sebuah wadah dari bidang data. Itu merangkum keadaan dan algoritma untuk memanipulasi keadaan itu. Apa yang penting dari pernyataan ini adalah bahwa negara seharusnya dienkapsulasi dan hanya dimanipulasi oleh algoritma yang disediakan oleh objek.
sbi

22

Saya tahu ini agak terlambat, tetapi saya pikir ada beberapa orang yang tertarik dengan kinerja.

Saya telah melakukan tes kinerja kecil. Saya menulis kelas "NumberHolder" yang, baik, memegang Integer. Anda dapat membaca Integer itu dengan menggunakan metode pengambil anInstance.getNumber()atau dengan langsung mengakses nomor dengan menggunakan anInstance.number. Program saya membaca angka 1.000.000.000 kali, melalui kedua cara. Proses itu diulang lima kali dan waktu dicetak. Saya mendapat hasil sebagai berikut:

Time 1: 953ms, Time 2: 741ms
Time 1: 655ms, Time 2: 743ms
Time 1: 656ms, Time 2: 634ms
Time 1: 637ms, Time 2: 629ms
Time 1: 633ms, Time 2: 625ms

(Waktu 1 adalah jalan langsung, Waktu 2 adalah pengambil)

Soalnya, rajin (hampir) selalu sedikit lebih cepat. Kemudian saya mencoba dengan jumlah siklus yang berbeda. Alih-alih 1 juta, saya menggunakan 10 juta dan 0,1 juta. Hasil:

10 juta siklus:

Time 1: 6382ms, Time 2: 6351ms
Time 1: 6363ms, Time 2: 6351ms
Time 1: 6350ms, Time 2: 6363ms
Time 1: 6353ms, Time 2: 6357ms
Time 1: 6348ms, Time 2: 6354ms

Dengan 10 juta siklus, waktunya hampir sama. Berikut adalah 100 ribu (0,1 juta) siklus:

Time 1: 77ms, Time 2: 73ms
Time 1: 94ms, Time 2: 65ms
Time 1: 67ms, Time 2: 63ms
Time 1: 65ms, Time 2: 65ms
Time 1: 66ms, Time 2: 63ms

Juga dengan jumlah siklus yang berbeda, rajin rajin sedikit lebih cepat daripada cara biasa. Saya harap ini membantu Anda.


3
Ada overhead "noticable" yang memiliki panggilan fungsi untuk mengakses memori alih-alih hanya memuat alamat objek dan menambahkan offset untuk mengakses anggota. Peluangnya adalah VM rata-rata mengoptimalkan pengambil Anda. Apapun, overhead yang disebutkan tidak layak kehilangan semua manfaat getter / setter.
Alex

16

Jangan gunakan setter getter kecuali diperlukan untuk pengiriman Anda saat ini. Jangan berpikir terlalu banyak tentang apa yang akan terjadi di masa depan, jika ada hal yang diubah dengan permintaan perubahan di sebagian besar aplikasi produksi, sistem.

Berpikir sederhana, mudah, tambahkan kerumitan saat dibutuhkan.

Saya tidak akan mengambil keuntungan dari ketidaktahuan pemilik bisnis dengan pengetahuan teknis yang mendalam bagaimana hanya karena saya pikir itu benar atau saya suka pendekatannya.

Saya memiliki sistem besar yang ditulis tanpa setter getter hanya dengan pengubah akses dan beberapa metode untuk memvalidasi dan melakukan logika biz. Jika Anda benar-benar membutuhkannya. Gunakan apa saja.


16

Kami menggunakan getter dan setter:

  • untuk dapat digunakan kembali
  • untuk melakukan validasi pada tahap pemrograman selanjutnya

Metode pengambil dan penyetel adalah antarmuka publik untuk mengakses anggota kelas privat.


Mantra enkapsulasi

Mantra enkapsulasi adalah membuat bidang pribadi dan metode publik.

Metode Getter: Kita bisa mendapatkan akses ke variabel pribadi.

Metode Setter: Kami dapat memodifikasi bidang pribadi.

Meskipun metode pengambil dan penyetel tidak menambahkan fungsi baru, kita dapat mengubah pikiran kita kembali nanti untuk membuat metode itu

  • lebih baik;
  • lebih aman; dan
  • lebih cepat.

Di mana pun nilai dapat digunakan, metode yang mengembalikan nilai itu dapat ditambahkan. Dari pada:

int x = 1000 - 500

menggunakan

int x = 1000 - class_name.getValue();

Dalam istilah awam

Representasi kelas "Orang"

Misalkan kita perlu menyimpan detail ini Person. Ini Personmemiliki bidang name, agedan sex. Melakukan ini melibatkan menciptakan metode untuk name, agedan sex. Sekarang jika kita perlu membuat orang lain, menjadi perlu untuk menciptakan metode untuk name, age, sexlagi.

Daripada melakukan ini, kita dapat membuat kacang class(Person)dengan metode pengambil dan penyetel. Jadi besok kita bisa membuat objek dari Bean ini class(Person class)kapan saja kita perlu menambahkan orang baru (lihat gambar). Jadi kami menggunakan kembali bidang dan metode kelas kacang, yang jauh lebih baik.


15

Saya menghabiskan cukup banyak waktu memikirkan ini untuk kasus Java, dan saya percaya alasan sebenarnya adalah:

  1. Kode untuk antarmuka, bukan implementasi
  2. Antarmuka hanya menentukan metode, bukan bidang

Dengan kata lain, satu-satunya cara Anda dapat menentukan bidang dalam antarmuka adalah dengan menyediakan metode untuk menulis nilai baru dan metode untuk membaca nilai saat ini.

Metode-metode tersebut adalah pengambil dan pembuat ...


1
Oke, pertanyaan kedua; dalam kasus di mana itu adalah proyek di mana Anda tidak mengekspor sumber kepada siapa pun, dan Anda memiliki kendali penuh atas sumber itu ... apakah Anda mendapatkan sesuatu dengan getter dan setter?
Dean J

2
Dalam setiap proyek Java non-sepele Anda perlu kode ke antarmuka untuk membuat hal-hal yang dapat dikelola dan diuji (pikirkan mockup dan objek proxy). Jika Anda menggunakan antarmuka, Anda memerlukan getter dan setter.
Thorbjørn Ravn Andersen

15

Ini bisa berguna untuk pemuatan malas. Katakanlah objek tersebut disimpan dalam database, dan Anda tidak ingin mendapatkannya kecuali Anda membutuhkannya. Jika objek diambil oleh pengambil, maka objek internal bisa nol sampai seseorang memintanya, maka Anda bisa mendapatkannya pada panggilan pertama ke pengambil.

Saya memiliki kelas halaman dasar dalam proyek yang diserahkan kepada saya yang memuat beberapa data dari beberapa panggilan layanan web yang berbeda, tetapi data dalam panggilan layanan web tersebut tidak selalu digunakan di semua halaman anak-anak. Layanan web, untuk semua manfaatnya, merintis definisi baru "lambat", jadi Anda tidak ingin membuat panggilan layanan web jika tidak perlu.

Saya pindah dari bidang publik ke getter, dan sekarang getter memeriksa cache, dan jika tidak ada, hubungi layanan web. Jadi dengan sedikit pembungkusan, banyak panggilan layanan web dicegah.

Jadi rajin rajin menyelamatkan saya dari mencoba mencari tahu, pada setiap halaman anak, apa yang saya butuhkan. Jika saya membutuhkannya, saya panggil pengambil, dan ia akan menemukannya untuk saya jika saya belum memilikinya.

    protected YourType _yourName = null;
    public YourType YourName{
      get
      {
        if (_yourName == null)
        {
          _yourName = new YourType();
          return _yourName;
        }
      }
    }

Jadi, apakah pengambil memanggil setter?
icc97

Saya telah menambahkan contoh kode tentang bagaimana saya melakukannya di masa lalu - pada dasarnya, Anda menyimpan kelas aktual di anggota yang dilindungi, kemudian mengembalikan anggota yang dilindungi itu di get accessor, menginisialisasi jika tidak diinisialisasi.
quillbreaker


13

Satu aspek yang saya lewatkan dalam jawaban sejauh ini, spesifikasi akses:

  • untuk anggota Anda hanya memiliki satu spesifikasi akses untuk pengaturan dan pengambilan
  • untuk setter dan getter Anda dapat menyetelnya dan mendefinisikannya secara terpisah

13

EDIT: Saya menjawab pertanyaan ini karena ada banyak orang yang belajar pemrograman menanyakan hal ini, dan sebagian besar jawabannya sangat kompeten secara teknis, tetapi mereka tidak mudah dimengerti jika Anda seorang pemula. Kami semua pemula, jadi saya pikir saya akan mencoba jawaban yang lebih ramah bagi pemula.

Dua yang utama adalah polimorfisme, dan validasi. Bahkan jika itu hanya struktur data yang bodoh.

Katakanlah kita memiliki kelas sederhana ini:

public class Bottle {
  public int amountOfWaterMl;
  public int capacityMl;
}

Kelas yang sangat sederhana yang menampung berapa banyak cairan di dalamnya, dan berapa kapasitasnya (dalam mililiter).

Apa yang terjadi ketika saya melakukannya:

Bottle bot = new Bottle();
bot.amountOfWaterMl = 1500;
bot.capacityMl = 1000;

Nah, Anda tidak akan berharap itu berhasil, bukan? Anda ingin ada semacam pemeriksaan kewarasan. Dan lebih buruknya, bagaimana jika saya tidak pernah menentukan kapasitas maksimum? Oh sayang, kami punya masalah.

Tapi ada masalah lain juga. Bagaimana jika botol hanya satu jenis wadah? Bagaimana jika kita memiliki beberapa wadah, semua dengan kapasitas dan jumlah cairan yang diisi? Jika kita bisa membuat antarmuka, kita bisa membiarkan sisa program kita menerima antarmuka itu, dan botol, jerigen dan segala macam hal hanya akan bekerja secara bergantian. Bukankah itu lebih baik? Karena metode permintaan antarmuka, ini juga merupakan hal yang baik.

Kami akan berakhir dengan sesuatu seperti:

public interface LiquidContainer {
  public int getAmountMl();
  public void setAmountMl(int amountMl);
  public int getCapacityMl();
}

Bagus! Dan sekarang kita hanya mengganti Botol dengan ini:

public class Bottle extends LiquidContainer {
  private int capacityMl;
  private int amountFilledMl;

  public Bottle(int capacityMl, int amountFilledMl) {
    this.capacityMl = capacityMl;
    this.amountFilledMl = amountFilledMl;
    checkNotOverFlow();
  }

  public int getAmountMl() {
    return amountFilledMl;
  }

  public void setAmountMl(int amountMl) {
     this.amountFilled = amountMl;
     checkNotOverFlow();
  }
  public int getCapacityMl() {
    return capacityMl;
  }

  private void checkNotOverFlow() {
    if(amountOfWaterMl > capacityMl) {
      throw new BottleOverflowException();
    }
}

Saya akan meninggalkan definisi BottleOverflowException sebagai latihan untuk pembaca.

Sekarang perhatikan betapa kuatnya ini. Kami dapat menangani semua jenis wadah dalam kode kami sekarang dengan menerima LiquidContainer, bukan Botol. Dan bagaimana botol-botol ini menangani hal-hal semacam ini bisa berbeda. Anda dapat memiliki botol yang menuliskan statusnya ke disk saat berubah, atau botol yang menghemat database SQL atau GNU tahu apa lagi.

Dan semua ini dapat memiliki cara berbeda untuk menangani berbagai waria. Botol hanya memeriksa dan jika meluap, ia melempar RuntimeException. Tapi itu mungkin hal yang salah untuk dilakukan. (Ada diskusi yang bermanfaat untuk dilakukan tentang penanganan kesalahan, tapi saya sengaja membuatnya sangat sederhana di sini. Orang-orang dalam komentar kemungkinan akan menunjukkan kekurangan dari pendekatan sederhana ini;;))

Dan ya, sepertinya kita beralih dari ide yang sangat sederhana untuk mendapatkan jawaban yang jauh lebih baik dengan cepat.

Harap dicatat juga bahwa Anda tidak dapat mengubah kapasitas botol. Sekarang diatur di atas batu. Anda bisa melakukan ini dengan int dengan menyatakannya final. Tetapi jika ini adalah daftar, Anda dapat mengosongkannya, menambahkan hal-hal baru ke dalamnya, dan seterusnya. Anda tidak dapat membatasi akses untuk menyentuh jeroan.

Ada juga hal ketiga yang tidak semua orang atasi: getter dan setter menggunakan panggilan metode. Itu berarti mereka terlihat seperti metode normal di tempat lain. Alih-alih memiliki sintaks khusus aneh untuk DTO dan yang lainnya, Anda memiliki hal yang sama di mana-mana.


2
Terima kasih atas penjelasan setengah layak dari antarmuka yang pernah saya baca, tidak ada referensi untuk "remote control" atau "mobil"
djvs

1
"Anda dapat memiliki botol yang menuliskan status mereka ke disk saat itu berubah, atau botol yang menghemat database SQL" Aku terkulai xD begitu keras. Tapi bagaimanapun, ide bagus untuk menyajikannya!
Xerus

10

Dalam bahasa yang tidak mendukung "properti" (C ++, Java) atau memerlukan kompilasi ulang klien saat mengubah bidang ke properti (C #), menggunakan metode get / set lebih mudah untuk dimodifikasi. Misalnya, menambahkan logika validasi ke metode setFoo tidak akan perlu mengubah antarmuka publik kelas.

Dalam bahasa yang mendukung properti "nyata" (Python, Ruby, mungkin Smalltalk?) Tidak ada gunanya untuk mendapatkan / mengatur metode.


1
Re: C #. Jika Anda menambahkan fungsionalitas ke get / set, bukankah itu membutuhkan kompilasi ulang?
steamer25

@ steamer25: maaf, salah ketik. Maksud saya, klien kelas harus dikompilasi ulang.
John Millikin

5
Menambahkan logika validasi ke metode setFoo tidak akan perlu mengubah antarmuka kelas di tingkat bahasa , tetapi itu mengubah antarmuka sebenarnya , alias kontrak, karena itu mengubah prasyarat. Mengapa seseorang ingin kompiler untuk tidak memperlakukan itu sebagai perubahan yang melanggar ketika itu ?
R. Martinho Fernandes

@ R. MartinhoFernandes bagaimana cara seseorang memperbaiki masalah "rusak" ini? Kompiler tidak dapat memastikan apakah rusak atau tidak. Ini hanya masalah ketika Anda menulis perpustakaan untuk orang lain, tetapi Anda menjadikannya sebagai zOMG universal di sini menjadi naga!
Phil

3
Membutuhkan kompilasi ulang, sebagaimana disebutkan dalam jawaban, adalah salah satu cara kompiler dapat membuat Anda menyadari kemungkinan perubahan yang melanggar. Dan hampir semua yang saya tulis secara efektif "perpustakaan untuk orang lain", karena saya tidak bekerja sendirian. Saya menulis kode yang memiliki antarmuka yang akan digunakan orang lain dalam proyek ini. Apa bedanya? Sial, bahkan jika saya akan menjadi pengguna antarmuka itu, mengapa saya harus memegang kode saya dengan standar kualitas yang lebih rendah? Saya tidak suka bekerja dengan antarmuka yang merepotkan, bahkan jika saya yang menulisnya.
R. Martinho Fernandes

6

Salah satu prinsip dasar desain OO: Enkapsulasi!

Ini memberi Anda banyak manfaat, salah satunya adalah Anda dapat mengubah implementasi pengambil / penyetel di belakang layar tetapi konsumen mana pun dari nilai itu akan terus bekerja selama tipe datanya tetap sama.


23
Penawaran enkapsulasi dan setter enkapsulasi sangat tipis. Lihat di sini .
sbi

Jika antarmuka publik Anda menyatakan bahwa 'foo' bertipe 'T' dan dapat diatur untuk apa saja, Anda tidak akan pernah bisa mengubahnya. Anda tidak dapat yang terakhir memutuskan untuk membuatnya dari tipe 'Y', Anda juga tidak dapat memaksakan aturan seperti batasan ukuran. Jadi, jika Anda memiliki publik / set yang tidak melakukan banyak set / mendapatkan, Anda mendapatkan apa-apa bahwa bidang publik tidak akan menawarkan dan membuatnya lebih rumit untuk digunakan. Jika Anda memiliki batasan, seperti objek dapat disetel ke nol, atau nilainya harus dalam rentang, maka ya, metode kumpulan publik akan diperlukan, tetapi Anda masih menunjukkan kontrak dari pengaturan nilai ini yang dapat ' t change
thecoshman

Mengapa kontrak tidak dapat berubah?
Phil

5
Mengapa semuanya harus terus dikompilasi jika kontrak berubah?
R. Martinho Fernandes

5

Anda harus menggunakan getter dan setter saat:

  • Anda sedang berhadapan dengan sesuatu yang secara konseptual merupakan atribut, tetapi:
    • Bahasa Anda tidak memiliki properti (atau mekanisme serupa, seperti jejak variabel Tcl), atau
    • Dukungan properti bahasa Anda tidak cukup untuk kasus penggunaan ini, atau
    • Konvensi idiomatik bahasa Anda (atau terkadang kerangka kerja Anda) mendorong getter atau setter untuk use case ini.

Jadi ini jarang sekali pertanyaan OO umum; itu adalah pertanyaan khusus bahasa, dengan jawaban berbeda untuk bahasa yang berbeda (dan berbagai kasus penggunaan).


Dari sudut pandang teori OO, getter dan setters tidak berguna. Antarmuka kelas Anda adalah fungsinya, bukan seperti apa kondisinya. (Jika tidak, Anda telah menulis kelas yang salah.) Dalam kasus yang sangat sederhana, di mana apa yang dilakukan kelas adalah adil, misalnya, mewakili titik dalam koordinat persegi panjang, * atribut adalah bagian dari antarmuka; getter dan setter hanya cloud itu. Tetapi dalam hal apa pun kecuali kasus yang sangat sederhana, baik atribut maupun getter dan setter bukan bagian dari antarmuka.

Dengan kata lain: Jika Anda percaya bahwa konsumen kelas Anda seharusnya tidak tahu bahwa Anda memiliki spamatribut, apalagi dapat mengubahnya dengan sendirinya, maka memberi mereka set_spammetode adalah hal terakhir yang ingin Anda lakukan.

* Bahkan untuk kelas sederhana itu, Anda mungkin tidak perlu ingin mengizinkan pengaturan xdan ynilai - nilai. Jika ini benar-benar kelas, tidak harus itu memiliki metode seperti translate, rotate, dll? Jika hanya kelas karena bahasa Anda tidak memiliki catatan / struct / nama tupel, maka ini bukan pertanyaan OO ...


Tapi tidak ada yang pernah melakukan desain OO umum. Mereka melakukan desain, dan implementasi, dalam bahasa tertentu. Dan dalam beberapa bahasa, getter dan setter jauh dari tidak berguna.

Jika bahasa Anda tidak memiliki properti, maka satu-satunya cara untuk mewakili sesuatu yang secara konseptual adalah atribut, tetapi sebenarnya dihitung, atau divalidasi, dll., Adalah melalui pengambil dan setter.

Bahkan jika bahasa Anda memang memiliki properti, mungkin ada kasus di mana bahasa tersebut tidak memadai atau tidak sesuai. Misalnya, jika Anda ingin membolehkan subkelas untuk mengontrol semantik atribut, dalam bahasa tanpa akses dinamis, subkelas tidak dapat menggantikan properti yang dikomputasi untuk atribut.

Adapun "bagaimana jika saya ingin mengubah implementasi saya nanti?" pertanyaan (yang diulang beberapa kali dalam susunan kata yang berbeda dalam pertanyaan OP dan jawaban yang diterima): Jika itu benar-benar perubahan implementasi murni, dan Anda mulai dengan atribut, Anda dapat mengubahnya ke properti tanpa mempengaruhi antarmuka. Kecuali, tentu saja, bahasa Anda tidak mendukung itu. Jadi ini benar-benar kasus yang sama lagi.

Juga, penting untuk mengikuti idiom bahasa (atau kerangka kerja) yang Anda gunakan. Jika Anda menulis kode gaya Ruby yang indah di C #, pengembang C # berpengalaman selain Anda akan kesulitan membacanya, dan itu buruk. Beberapa bahasa memiliki budaya yang lebih kuat di sekitar konvensi mereka daripada yang lain. — dan mungkin bukan kebetulan bahwa Jawa dan Python, yang berada di ujung yang berlawanan dari spektrum untuk bagaimana getter idiomatik, kebetulan memiliki dua budaya terkuat.

Di luar pembaca manusia, akan ada perpustakaan dan alat yang mengharapkan Anda untuk mengikuti kebaktian, dan membuat hidup Anda lebih sulit jika tidak. Mengaitkan widget Builder Interface ke apa pun selain properti ObjC, atau menggunakan perpustakaan Java mengejek tertentu tanpa getter, hanya membuat hidup Anda lebih sulit. Jika alat itu penting bagi Anda, jangan melawannya.



3

Metode getter dan setter adalah metode accessor, artinya mereka umumnya merupakan antarmuka publik untuk mengubah anggota kelas privat. Anda menggunakan metode pengambil dan penyetel untuk menentukan properti. Anda mengakses metode pengambil dan penyetel sebagai properti di luar kelas, meskipun Anda mendefinisikannya di dalam kelas sebagai metode. Properti-properti di luar kelas dapat memiliki nama yang berbeda dari nama properti di kelas.

Ada beberapa keuntungan menggunakan metode pengambil dan penyetel, seperti kemampuan untuk membiarkan Anda membuat anggota dengan fungsionalitas canggih yang dapat Anda akses seperti properti. Mereka juga memungkinkan Anda membuat properti hanya-baca dan menulis-saja.

Meskipun metode pengambil dan penyetel berguna, Anda harus berhati-hati untuk tidak menggunakannya secara berlebihan karena, di antara masalah lain, mereka dapat membuat pemeliharaan kode lebih sulit dalam situasi tertentu. Juga, mereka menyediakan akses ke implementasi kelas Anda, seperti anggota publik. Praktik OOP menghambat akses langsung ke properti dalam kelas.

Ketika Anda menulis kelas, Anda selalu didorong untuk membuat sebanyak mungkin variabel instan Anda menjadi pribadi dan menambahkan metode pengambil dan penyetel yang sesuai. Ini karena ada beberapa kali ketika Anda mungkin tidak ingin membiarkan pengguna mengubah variabel tertentu dalam kelas Anda. Misalnya, jika Anda memiliki metode statis pribadi yang melacak jumlah instance yang dibuat untuk kelas tertentu, Anda tidak ingin pengguna memodifikasi penghitung itu menggunakan kode. Hanya pernyataan konstruktor yang harus menambah variabel itu setiap kali dipanggil. Dalam situasi ini, Anda dapat membuat variabel instance pribadi dan mengizinkan metode pengambil hanya untuk variabel penghitung, yang berarti pengguna dapat mengambil nilai saat ini hanya dengan menggunakan metode pengambil, dan mereka tidak akan dapat menetapkan nilai baru menggunakan metode setter.


3

Kode berkembang . privatesangat bagus untuk ketika Anda membutuhkan perlindungan anggota data . Akhirnya semua kelas harus berupa "miniprogram" yang memiliki antarmuka yang terdefinisi dengan baik sehingga Anda tidak bisa mengacaukan internalnya .

Yang mengatakan, pengembangan perangkat lunak bukan tentang menetapkan versi akhir kelas seolah-olah Anda menekan beberapa patung besi pada percobaan pertama. Saat Anda bekerja dengannya, kode lebih seperti tanah liat. Ini berkembang saat Anda mengembangkannya dan mempelajari lebih lanjut tentang domain masalah yang Anda selesaikan. Selama kelas pengembangan dapat berinteraksi satu sama lain dari yang seharusnya (ketergantungan Anda berencana faktor keluar), bergabung bersama, atau berpisah. Jadi saya pikir perdebatan bermuara pada orang-orang yang tidak ingin menulis secara religius

int getVar() const { return var ; }

Jadi kamu punya:

doSomething( obj->getVar() ) ;

Dari pada

doSomething( obj->var ) ;

Tidak hanya getVar()berisik secara visual, ini memberikan ilusi yang gettingVar()entah bagaimana merupakan proses yang lebih kompleks daripada yang sebenarnya. Bagaimana Anda (sebagai penulis kelas) menganggap kesucian varini sangat membingungkan bagi pengguna kelas Anda jika memiliki passthru setter - maka sepertinya Anda memasang gerbang ini untuk "melindungi" sesuatu yang Anda anggap berharga, (kesucian dari var) tetapi bahkan Anda mengakui varperlindungan tidak banyak berarti dengan kemampuan bagi siapa pun untuk hanya masuk dan set varuntuk nilai apa pun yang mereka inginkan, tanpa Anda bahkan mengintip apa yang mereka lakukan.

Jadi saya memprogram sebagai berikut (dengan asumsi pendekatan tipe "gesit" - yaitu ketika saya menulis kode tidak tahu persis apa yang akan dilakukan / tidak punya waktu atau pengalaman untuk merencanakan set antarmuka gaya air terjun yang rumit):

1) Mulai dengan semua anggota publik untuk objek dasar dengan data dan perilaku. Inilah sebabnya mengapa dalam semua kode "contoh" C ++ saya akan melihat saya menggunakan structalih-alih di classmana - mana.

2) Ketika perilaku internal suatu objek untuk anggota data menjadi cukup kompleks, (misalnya, ia suka menjaga internal std::listdalam beberapa jenis urutan), fungsi tipe pengaksesor ditulis. Karena saya pemrograman sendiri, saya tidak selalu mengatur anggota secara privatelangsung, tetapi di suatu tempat evolusi kelas anggota akan "dipromosikan" ke salah satu protectedatau private.

3) Kelas yang sepenuhnya diubah dan memiliki aturan ketat tentang internal mereka (yaitu mereka tahu persis apa yang mereka lakukan, dan Anda tidak boleh "bercinta" (istilah teknis) dengan internalnya) diberi classpenunjukan, anggota pribadi default, dan hanya beberapa anggota terpilih yang diizinkan public.

Saya menemukan pendekatan ini memungkinkan saya untuk menghindari duduk di sana dan menulis rajin rajin / giat ketika banyak anggota data bermigrasi keluar, bergeser sekitar, dll selama tahap awal evolusi kelas.


1
"... antarmuka yang terdefinisi dengan baik yang tidak bisa Anda abaikan dengan internal" dan validasi dalam setter.
Agi Hammerthief

3

Ada alasan bagus untuk mempertimbangkan menggunakan pengakses adalah tidak ada warisan properti. Lihat contoh berikut:

public class TestPropertyOverride {
    public static class A {
        public int i = 0;

        public void add() {
            i++;
        }

        public int getI() {
            return i;
        }
    }

    public static class B extends A {
        public int i = 2;

        @Override
        public void add() {
            i = i + 2;
        }

        @Override
        public int getI() {
            return i;
        }
    }

    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.i);
        a.add();
        System.out.println(a.i);
        System.out.println(a.getI());
    }
}

Keluaran:

0
0
4

3

Getters dan setters digunakan untuk mengimplementasikan dua aspek mendasar dari Pemrograman Berorientasi Objek yaitu:

  1. Abstraksi
  2. Enkapsulasi

Misalkan kita memiliki kelas Karyawan:

package com.highmark.productConfig.types;

public class Employee {

    private String firstName;
    private String middleName;
    private String lastName;

    public String getFirstName() {
      return firstName;
    }
    public void setFirstName(String firstName) {
       this.firstName = firstName;
    }
    public String getMiddleName() {
        return middleName;
    }
    public void setMiddleName(String middleName) {
         this.middleName = middleName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getFullName(){
        return this.getFirstName() + this.getMiddleName() +  this.getLastName();
    }
 }

Di sini rincian implementasi Nama Lengkap disembunyikan dari pengguna dan tidak dapat diakses langsung ke pengguna, tidak seperti atribut publik.


1
Bagi saya memiliki banyak getter dan setter yang tidak melakukan sesuatu yang unik tidak berguna. getFullName adalah pengecualian karena ia melakukan sesuatu yang lain. Memiliki hanya tiga variabel publik kemudian menjaga getFullName akan membuat program lebih mudah dibaca tetapi masih menyembunyikan nama lengkap itu. Secara umum saya baik-baik saja dengan getter dan setter jika a. mereka melakukan sesuatu yang unik dan / atau b. Anda hanya punya satu, ya Anda bisa memiliki final publik dan semua itu kecuali nah
FacelessTiger

1
Manfaatnya adalah Anda dapat mengubah internal kelas, tanpa mengubah antarmuka. Katakanlah, alih-alih tiga properti, Anda memiliki satu - array string. Jika Anda telah menggunakan getter dan setter, Anda dapat melakukan perubahan itu, dan kemudian memperbarui getter / setter untuk mengetahui bahwa nama [0] adalah nama depan, nama [1] di tengah, dll. Tetapi jika Anda hanya menggunakan properti publik , Anda juga harus mengubah setiap kelas yang diakses Karyawan, karena properti firstName yang mereka gunakan sudah tidak ada lagi.
Andrew Hows

1
@AndrewHow dari apa yang saya lihat dalam kehidupan nyata, ketika orang mengubah internal kelas, mereka juga mengubah antarmuka dan melakukan refactoring besar pada semua kode
Eildosa

2

Satu kegunaan lain (dalam bahasa yang mendukung properti) adalah bahwa setter dan getter dapat menyiratkan bahwa operasi adalah non-sepele. Biasanya, Anda ingin menghindari melakukan apa pun yang mahal secara komputasi di sebuah properti.


Saya tidak pernah berharap pengambil atau penyetel menjadi operasi yang mahal. Dalam kasus seperti itu, lebih baik gunakan pabrik: gunakan semua seter yang Anda butuhkan dan gunakan metode executeatau harga yang mahal build.
Hubert Grzeskowiak

2

Satu keuntungan relatif modern dari pengambil / setter adalah membuatnya lebih mudah untuk menelusuri kode dalam editor kode yang ditandai (diindeks). Misalnya. Jika Anda ingin melihat siapa yang menetapkan anggota, Anda dapat membuka hierarki panggilan penyetel.

Di sisi lain, jika anggota bersifat publik, alat tidak memungkinkan untuk memfilter akses baca / tulis ke anggota. Jadi, Anda harus berjalan dengan susah payah melalui semua penggunaan anggota.


Anda dapat melakukan klik kanan> temukan penggunaan pada anggota persis seperti pada pengambil / penyetel
Eildosa

2

Dalam bahasa berorientasi objek, metode, dan pengubah aksesnya, menyatakan antarmuka untuk objek itu. Antara konstruktor dan metode accessor dan mutator adalah mungkin bagi pengembang untuk mengontrol akses ke keadaan internal suatu objek. Jika variabel hanya dinyatakan publik maka tidak ada cara untuk mengatur akses itu. Dan ketika kita menggunakan setter kita dapat membatasi pengguna untuk input yang kita butuhkan. Berarti umpan untuk variabel tersebut akan datang melalui saluran yang tepat dan saluran sudah ditentukan oleh kami. Jadi lebih aman menggunakan setter.


1

Selain itu, ini untuk "bukti masa depan" kelas Anda. Secara khusus, mengubah dari bidang ke properti adalah jeda ABI, jadi jika Anda kemudian memutuskan bahwa Anda membutuhkan lebih banyak logika daripada hanya "mengatur / mendapatkan bidang", maka Anda perlu memecahkan ABI, yang tentu saja menimbulkan masalah untuk apa pun lain sudah dikompilasi dengan kelas Anda.


2
Saya kira mengubah perilaku pengambil atau penyetel bukan merupakan perubahan besar saat itu. </sarcasm>
R. Martinho Fernandes

@ R. MartinhoFernandes tidak harus selalu begitu. TBYS
Phil

1

Saya hanya ingin membuang gagasan anotasi: @getter dan @setter. Dengan @getter, Anda harus dapat obj = class.field tetapi tidak class.field = obj. Dengan @setter, dan sebaliknya. Dengan @getter dan @setter Anda harus dapat melakukan keduanya. Ini akan menjaga enkapsulasi dan mengurangi waktu dengan tidak memanggil metode sepele saat runtime.


1
Ini akan diimplementasikan saat runtime dengan "metode sepele". Sebenarnya, mungkin tidak sepele.
Phil

Hari ini ini dapat dilakukan dengan preprocessor anotasi.
Thorbjørn Ravn Andersen
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.