Kapan Getters and Setters dibenarkan

175

Getters dan setters sering dikritik sebagai OO tidak tepat. Di sisi lain, sebagian besar kode OO yang saya lihat memiliki getter dan setter yang luas.

Kapan getter dan setter dibenarkan? Apakah Anda mencoba menghindari menggunakannya? Apakah mereka terlalu sering digunakan?

Jika bahasa favorit Anda memiliki properti (milik saya) maka hal-hal seperti itu juga dianggap getter dan setter untuk pertanyaan ini. Mereka adalah hal yang sama dari perspektif metodologi OO. Mereka hanya memiliki sintaks yang lebih bagus.

Sumber untuk Pengritik Getter / Setter (beberapa diambil dari komentar untuk memberikan visibilitas yang lebih baik):

Untuk menyatakan kritik secara sederhana: Getters and Setters memungkinkan Anda untuk memanipulasi keadaan internal objek dari luar objek. Ini melanggar enkapsulasi. Hanya objek itu sendiri yang harus peduli dengan keadaan internalnya.

Dan contoh kode versi prosedural.

struct Fridge
{
    int cheese;
}

void go_shopping(Fridge fridge)
{
     fridge.cheese += 5;
}

Versi kode mutator:

class Fridge
{
     int cheese;

     void set_cheese(int _cheese) { cheese = _cheese; }
     int get_cheese() { return cheese; }
 }

void go_shopping(Fridge fridge)
{
     fridge.set_cheese(fridge.get_cheese() + 5);        
}

Para pengambil dan pembuat membuat kode jauh lebih rumit tanpa memberikan enkapsulasi yang tepat. Karena keadaan internal dapat diakses oleh objek lain kami tidak mendapatkan banyak dengan menambahkan getter dan setter ini.

Pertanyaan sebelumnya telah dibahas pada Stack Overflow:

Winston Ewert
sumber
21
Getters and setters are often criticized as being not proper OO- Tolong kutip.
Robert Harvey
45
Sob, mengapa karena punya kode? Ini pertanyaan bagus dan berbau perang suci jika dianggap serius.
Peter Turner
2
@ Winston Ewert: "... pertanyaannya adalah apakah Anda harus mengakses data di dalam kelas sama sekali.": Well, tidak ada yang memaksa Anda untuk menerapkan getter dan setter untuk semua variabel anggota, Anda hanya mengimplementasikan yang Anda butuhkan. Itu sebabnya Anda menggunakan getter dan setter alih-alih variabel anggota publik.
Giorgio
2
@ Winston Ewert: Saya tidak melihat bagaimana tidak adanya getter / setter akan menyelesaikan masalah ini: harus ada cara untuk mengakses setiap bagian data jika tidak maka tidak ada gunanya; tugas desain yang bagus untuk memutuskan bagian mana dari data yang dapat diakses oleh bagian mana dari kode. Jika seseorang adalah desainer yang buruk, dia akan menjadi seperti itu dengan atau tanpa pengambil dan penentu. Hanya 2 sen saya.
Giorgio

Jawaban:

162

Memiliki getter dan setter tidak dengan sendirinya merusak enkapsulasi. Apa yang memecahkan enkapsulasi secara otomatis menambahkan pengambil dan penyetel untuk setiap anggota data (setiap bidang , dalam istilah java), tanpa memikirkannya. Meskipun ini lebih baik daripada membuat semua anggota data publik, itu hanya satu langkah kecil.

Maksud enkapsulasi bukanlah bahwa Anda seharusnya tidak dapat mengetahui atau mengubah keadaan objek dari luar objek, tetapi bahwa Anda harus memiliki kebijakan yang masuk akal untuk melakukannya.

  • Beberapa anggota data mungkin sepenuhnya internal ke objek, dan seharusnya tidak memiliki getter atau setter.

  • Beberapa anggota data harus hanya-baca, sehingga mereka mungkin membutuhkan getter tetapi tidak setter.

  • Beberapa anggota data mungkin harus tetap konsisten satu sama lain. Dalam kasus seperti itu Anda tidak akan menyediakan setter untuk masing-masing, tetapi metode tunggal untuk mengaturnya pada saat yang sama, sehingga Anda dapat memeriksa nilai-nilai untuk konsistensi.

  • Beberapa anggota data mungkin hanya perlu diubah dengan cara tertentu, seperti bertambah atau dikurangi dengan jumlah yang tetap. Dalam hal ini, Anda akan memberikan metode increment()dan / atau decrement(), bukannya setter.

  • Namun yang lain mungkin benar-benar perlu membaca-menulis, dan akan memiliki baik pengambil dan pembuat.

Pertimbangkan contoh a class Person. Katakanlah seseorang memiliki nama, nomor jaminan sosial, dan usia. Katakanlah kita tidak mengizinkan orang mengubah nama atau nomor jaminan sosial mereka. Namun, usia seseorang harus bertambah 1 setiap tahun. Dalam hal ini, Anda akan memberikan konstruktor yang akan menginisialisasi nama dan SSN ke nilai yang diberikan, dan yang akan menginisialisasi usia menjadi 0. Anda juga akan memberikan metode incrementAge(), yang akan menambah usia dengan 1. Anda juga akan memberikan getter untuk ketiganya. Tidak ada setter diperlukan dalam kasus ini.

Dalam desain ini Anda membiarkan keadaan objek diperiksa dari luar kelas, dan Anda membiarkannya diubah dari luar kelas. Namun, Anda tidak mengizinkan negara diubah secara sewenang-wenang. Ada kebijakan, yang secara efektif menyatakan bahwa nama dan SSN tidak dapat diubah sama sekali, dan bahwa usia dapat bertambah 1 tahun sekaligus.

Sekarang katakanlah seseorang juga memiliki gaji. Dan orang-orang dapat berganti pekerjaan sesuka hati, yang berarti gajinya juga akan berubah. Untuk memodelkan situasi ini kita tidak memiliki cara lain selain memberikan setSalary()metode! Mengizinkan gaji diubah sesuka hati adalah kebijakan yang masuk akal untuk kasus ini.

By the way, dalam contoh Anda, saya akan memberikan kelas Fridgeyang putCheese()dan takeCheese()metode, bukan get_cheese()dan set_cheese(). Maka Anda masih akan memiliki enkapsulasi.


public class Fridge {
  private List objects;
  private Date warranty;

  /** How the warranty is stored internally is a detail. */
  public Fridge( Date warranty ) {
    // The Fridge can set its internal warranty, but it is not re-exposed.
    setWarranty( warranty );
  }

  /** Doesn't expose how the fridge knows it is empty. */
  public boolean isEmpty() {
    return getObjects().isEmpty();
  }

  /** When the fridge has no more room... */
  public boolean isFull() {
  }

  /** Answers whether the given object will fit. */
  public boolean canStore( Object o ) {
    boolean result = false;

    // Clients may not ask how much room remains in the fridge.
    if( o instanceof PhysicalObject ) {
      PhysicalObject po = (PhysicalObject)o;

      // How the fridge determines its remaining usable volume is a detail.
      // How a physical object determines whether it fits within a specified
      // volume is also a detail.
      result = po.isEnclosedBy( getUsableVolume() );
    }

     return result;
  }

  /** Doesn't expose how the fridge knows its warranty has expired. */
  public boolean isPastWarranty() {
    return getWarranty().before( new Date() );
  }

  /** Doesn't expose how objects are stored in the fridge. */
  public synchronized void store( Object o ) {
    validateExpiration( o );

    // Can the object fit?
    if( canStore( o ) ) {
      getObjects().add( o );
    }
    else {
      throw FridgeFullException( o );
    }
  }

  /** Doesn't expose how objects are removed from the fridge. */
  public synchronized void remove( Object o ) {
    if( !getObjects().contains( o ) ) {
      throw new ObjectNotFoundException( o );
    }

    getObjects().remove( o );

    validateExpiration( o );
  }

  /** Lazily initialized list, an implementation detail. */
  private synchronized List getObjects() {
    if( this.list == null ) { this.list = new List(); }
    return this.list;
  }

  /** How object expiration is determined is also a detail. */
  private void validateExpiration( Object o ) {
    // Objects can answer whether they have gone past a given
    // expiration date. How each object "knows" it has expired
    // is a detail. The Fridge might use a scanner and
    // items might have embedded RFID chips. It's a detail hidden
    // by proper encapsulation.
    if( o implements Expires && ((Expires)o).expiresBefore( today ) ) {
      throw new ExpiredObjectException( o );
    }
  }

  /** This creates a copy of the warranty for immutability purposes. */
  private void setWarranty( Date warranty ) {
    assert warranty != null;
    this.warranty = new Date( warranty.getTime() )
  }
}
Dave Jarvis
sumber
4
Tolong jangan mengambil contoh Kulkas saya terlalu serius. :) Sebuah lemari es yang sebenarnya seharusnya menjadi objek kontainer, saya berharap itu tahu cara memegang benda tanpa khawatir tentang apa sebenarnya mereka. Yaitu seperti ArrayList. Adapun apa yang membuatnya menjadi Kulkas, katakanlah itu membuat serialisasi objek ke disk saat disimpan sehingga mereka akan selamat dari kegagalan sistem. Baik. Cukup mengambil contoh kulkas saya dengan serius.
Winston Ewert
6
Tapi bukankah lemari es harus tahu tanggal kedaluwarsa, sehingga bisa memberi tahu Anda bahwa keju sudah memburuk dan harus dibuang? :) Ok, kita harus hentikan ini! :)
Dima
10
Diskusi yang cukup menarik tentang Logika Kulkas yang Anda buat di sini ...
Mason Wheeler
35
Haha, saya ingin melihat iklan untuk itu: "Ini adalah jenis baru dari lemari es: Ini melempar sesuatu kepada Anda ketika mencoba untuk mengambil sesuatu yang tidak ada! Dengan cara ini Anda hanya akan mencobanya sekali! Anda khawatir tentang mengisi kulkas, dan biarkan kulkas khawatir tentang perilaku ilegal! "
gablin
42
Contohnya semuanya salah. Seharusnya tidak ada bidang Usia atau metode setAge (). Usia adalah fungsi dari tanggal lahir Orang dibandingkan dengan beberapa titik waktu. Meskipun tampaknya sepele, ini persis apa yang salah dengan praktik modern tentang mutabilitas penuh objek dan desain buruk lainnya, seperti yang terlihat dengan metode get / set untuk bidang pribadi versus dengan hati-hati mempertimbangkan apa yang benar-benar bisa berubah, apa itu bidang, apa objek harus tahu tentang perilakunya dan perubahan status apa yang sebenarnya valid (yang metode setX benar-benar hancurkan).
Darrell Teague
41

Alasan dasar untuk getter dan setter di Jawa sangat sederhana:

  • Anda hanya bisa menentukan metode , bukan bidang, dalam antarmuka.

Oleh karena itu, jika Anda ingin mengizinkan bidang untuk melewati antarmuka, Anda akan memerlukan metode pembaca dan penulis. Ini secara tradisional disebut getX dan setX untuk bidang x.

pengguna1249
sumber
44
Itu adalah batasan bahasa. Pertanyaan sebenarnya adalah apakah kita harus membiarkan benda lain memanipulasi keadaan itu.
Winston Ewert
3
@Peter Turner, jelas tidak ada yang mencegah bahasa memiliki antarmuka yang menyertakan properti. Di bawah penutup yang mungkin diimplementasikan sebagai pengambil / penyetel tetapi akan cukup mudah untuk menambahkan dukungan ke definisi antarmuka untuk properti.
Winston Ewert
2
@ Winston, pada akhirnya Anda harus mengizinkan kelas untuk meneruskan informasi bolak-balik antara satu sama lain untuk benar-benar menyelesaikan pekerjaan. Apa yang akan Anda sarankan sebagai gantinya?
6
Sebuah objek seharusnya menyediakan antarmuka level yang lebih tinggi untuk jeroan. Getters and Setters cenderung merupakan antarmuka tingkat rendah. Sebagai contoh, misalkan Anda memiliki kelas yang mengimplementasikan pohon biner. Anda bisa memiliki fungsi seperti GetLeft (), GetRight (), GetValue (), dan GetKey () untuk menavigasi pohon. Tetapi itu adalah pendekatan yang sepenuhnya salah. Kelas pohon biner Anda harus menyediakan operasi seperti Cari, Sisipkan, Hapus.
Winston Ewert
6
Untuk mengambil contoh lain, pertimbangkan sepotong tetris. Potongan tetris memiliki beberapa kondisi internal seperti block_type, rotasi, posisi. Anda bisa mendapatkan getter seperti GetBlockType (), GetRotation (), GetPosition (). Tetapi Anda seharusnya tidak melakukannya. Apa yang harus Anda miliki adalah metode GetSquares () yang mengembalikan daftar semua kotak yang ditempati oleh bagian ini. Anda juga seharusnya tidak memiliki hal-hal seperti SetPosition () atau SetRotation (). Sebagai gantinya, Anda harus memiliki operasi seperti MoveLeft (), MoveRight (), Rotate (), Fall ().
Winston Ewert
20

Dari http://www.adam-bien.com/roller/abien/entry/encapsulation_violation_with_getters_and

Gaya JavaBean:

connection.setUser("dukie");
connection.setPwd("duke");
connection.initialize();

OO-style:

connection.connect("dukie","duke");

Yah, jelas saya lebih suka pendekatan yang terakhir; itu tidak berdarah detail implementasi, itu lebih sederhana dan lebih ringkas, dan semua informasi yang diperlukan disertakan dengan pemanggilan metode, sehingga lebih mudah untuk memperbaikinya. Saya juga lebih suka mengatur anggota pribadi menggunakan parameter di konstruktor, bila memungkinkan.

Pertanyaan Anda adalah, kapan seorang pengambil dan pembuat dibenarkan? Mungkin ketika perubahan mode diperlukan, atau Anda perlu menginterogasi objek untuk beberapa informasi.

myObject.GetStatus();
myObject.SomeCapabilitySwitch = true;

Dalam memikirkannya, ketika saya pertama kali mulai membuat kode dalam C #, saya menulis banyak kode dengan gaya bahasa Jawa yang digambarkan di atas. Tetapi ketika saya memperoleh pengalaman dalam bahasa, saya mulai melakukan lebih banyak pengaturan anggota dalam konstruktor, dan menggunakan metode yang lebih mirip gaya OO di atas.

Robert Harvey
sumber
6
Mengapa "berikan semua argumen sebagai parameter" ala OO?
5
Sementara "OO-style" Anda baik-baik saja untuk 2 atau 3 pilihan, saya lebih suka gaya JavaBean setelah Anda mendapatkan di atas itu. Memiliki 20 metode kelebihan untuk setiap kombinasi parameter yang mungkin hanya tampak mengerikan
TheLQ
1
@TheLQ: C # 4.0 membuat ini lebih mudah dengan memungkinkan parameter opsional dan bernama dalam panggilan konstruktor dan metode.
Robert Harvey
7
@TheLQ Itulah gunanya Builder dengan sintaks fasih, lihat misalnya MapMaker Guava .
maaartinus
5
@ Thorbjørn Ravn Andersen, saya akan mengatakan "berikan semua argumen sebagai parameter" adalah gaya OO yang lebih baik daripada "panggil JavaBean setters" karena setter menyiratkan ada atribut mendasar yang sedang diatur, yang menurut definisi memperlihatkan detail internal. Menggunakan metode connect (..) dalam contoh tidak membuat asumsi seperti itu; mungkin Anda menetapkan atribut pengguna dan kata sandi, mungkin Anda tidak. Yang penting adalah Anda fokus pada konsep OO yang tepat: pesan "hubungkan".
Andres F.
13

Kapan getter dan setter dibenarkan?

Ketika perilaku "get" dan "set" benar-benar cocok dengan perilaku dalam model Anda, yang praktis tidak pernah .

Setiap penggunaan lainnya hanyalah cheat karena perilaku domain bisnis tidak jelas.

Sunting

Jawaban itu mungkin terdengar kurang ajar, jadi izinkan saya memperluas. Jawaban di atas sebagian besar benar tetapi fokus pada paradigma pemrograman desain OO dan beberapa yang melewatkan gambaran besar. Dalam pengalaman saya ini membuat orang berpikir bahwa menghindari gettings dan setters adalah beberapa aturan akademik untuk bahasa pemrograman OO (misalnya orang berpikir mengganti getter dengan properti adalah hal yang bagus)

Sebenarnya itu adalah konsekuensi dari proses desain. Anda tidak harus masuk ke antarmuka dan enkapsulasi dan pola, berdebat apakah itu merusak atau tidak paradigma ini dan apa atau tidak pemrograman OO yang baik. Satu-satunya hal yang pada akhirnya penting adalah bahwa jika tidak ada apa pun di domain Anda yang berfungsi seperti ini dengan meletakkannya di Anda, bukan memodelkan domain Anda.

Kenyataannya adalah bahwa sangat tidak mungkin bahwa sistem di ruang domain Anda memiliki getter dan setter. Anda tidak dapat menghampiri pria atau wanita yang bertanggung jawab atas penggajian dan hanya mengatakan "Setel gaji ini ke X" atau "Ambilkan saya gaji ini" . Perilaku seperti itu tidak ada

Jika Anda memasukkan ini ke dalam kode Anda, Anda tidak mendesain sistem Anda agar sesuai dengan model domain Anda. Ya itu merusak antarmuka dan enkapsulasi, tetapi bukan itu intinya. Intinya adalah Anda memodelkan sesuatu yang tidak ada.

Terlebih lagi Anda mungkin kehilangan langkah atau proses penting, karena mungkin ada alasan saya tidak bisa hanya berjalan untuk membayar dan mengatakan menetapkan gaji ini ke X.

Ketika orang menggunakan getter dan setter, mereka cenderung mendorong aturan untuk proses ini ke tempat yang salah. Ini semakin menjauh dari domain Anda. Menggunakan contoh dunia nyata, itu seperti gaji dengan asumsi bahwa orang yang berjalan secara acak memiliki izin untuk mendapatkan nilai-nilai ini jika tidak dia tidak akan meminta mereka. Bukan hanya bukan karena domain itu, sebenarnya berbohong tentang bagaimana domain itu.

Cormac Mulhall
sumber
ini sepertinya tidak menawarkan sesuatu yang substansial atas poin-poin yang dibuat dan dijelaskan dalam 17 jawaban sebelumnya
nyamuk
1
Jawaban lain berbicara tentang objek, antarmuka dan paradigma bahasa lainnya. Yang baik-baik saja, tetapi itu bukan masalah utama. Getters dan setter tidak hanya buruk karena mereka melanggar beberapa aturan pengkodean, tetapi terutama karena Anda hampir tidak pernah menemukan perilaku ini dalam model aktual yang Anda wakili. Poin ini cenderung hilang dalam diskusi tentang praktik pengkodean terbaik.
Cormac Mulhall
Menurut Anda, apa antarmuka untuk penggajian adalah jika itu bukan set/getgaji?
Winston Ewert
1
Jika mereka dililit lapisan otorisasi dan terbatas pada entitas eksternal tertentu mereka bukan pengambil dan pembuat. Saya tidak mengatakan tidak memiliki metode di departemen penggajian yang mengubah gaji, tetapi metode itu harus sejalan dengan proses internal yang ada di perusahaan itu sendiri. Misalnya sebagian besar perusahaan menetapkan gaji berdasarkan kontrak karyawan, yang disahkan oleh manajer. Jika gaji karyawan berubah, kontrak baru dibuat dan manajer harus menandatanganinya. Oleh karena itu penggajian harus mengharapkan objek kontrak dan beberapa objek otorisasi untuk mengubah daftar gaji
Cormac Mulhall
3
Atau dengan kata lain, ada cukup perbedaan antara set_salary (new_salary, employee_id) dan berwenang_salary_update (employee_contract, authorising_manager). Proses tersebut harus memodelkan proses bisnis internal
Cormac Mulhall
11

Sebagai aturan umum, getter dan setter adalah ide yang buruk. Jika bidang tidak secara logis bagian dari antarmuka dan Anda menjadikannya pribadi, tidak apa-apa. Jika secara logis bagian dari antarmuka dan Anda membuatnya publik, itu bagus. Tetapi jika Anda menjadikannya pribadi dan kemudian berbalik dan menjadikannya publik secara efektif lagi dengan menyediakan pengambil dan penyetel, Anda kembali ke tempat Anda memulai kecuali kode Anda sekarang lebih bertele-tele dan dikaburkan.

Jelas, ada pengecualian. Di Jawa, Anda mungkin perlu menggunakan antarmuka. Pustaka standar Java memiliki persyaratan kompatibilitas yang sangat ekstrem sehingga melebihi ukuran kualitas kode yang normal. Bahkan mungkin Anda benar-benar berurusan dengan kasus yang legendaris namun langka di mana ada peluang bagus Anda nantinya dapat mengganti bidang yang tersimpan dengan perhitungan sambil jalan tanpa melanggar antarmuka. Tapi ini pengecualian. Getters dan setter adalah anti-pola yang membutuhkan pembenaran khusus.

rwallace
sumber
6
Seorang pengambil dan penyetel berarti bahwa atribut, bukan bidang, adalah bagian dari antarmuka. Misalkan getBirthDate () dan SetBirthDate () ambil "yyyy / mm / dd" tetapi bidang yang disimpan adalah jumlah hari sejak 01/01/1000. Anda dapat mengubahnya menjadi 01/01/0001 dan pengambil dan penyetel akan menjaga antarmuka tetap sama. Terutama karena 'publik' berarti dapat ditelusuri secara publik, variabel tidak boleh publik; selalu gunakan setter untuk memvalidasi nilai yang masuk, perbarui bidang terkait, konversi seperlunya, dll. Cobalah untuk menggunakan pengambil jika seandainya penyimpanan internal berubah.
Andy Canfield
@AndyCanfield itu agak kuat untuk mengatakan publik berarti yang dapat ditelusuri secara publik..itu hanya jika Anda memprogramnya dengan buruk. Jika Anda memiliki penyetel dan nilai yang salah dikirim ke sana maka Anda bisa melempar pengecualian yang bisa Anda sebut trashing program tetapi itu berhenti dengan kesalahan juga. Jika pengguna objek dapat memberikan nilai variabel dan cud memberikannya salah nilai maka Anda dapat mengingatnya dan mengujinya. Anda mungkin mengatakan "ah tapi saya hanya perlu melakukan 1 tes jika saya menggunakan setter", tapi mungkin kode Anda sendiri memberikan variabel nilai yang buruk..jadi setter mungkin tidak kurangi tests.n Anda dengan tes, prog Anda tidak "dibuang"
barlop
3

apakah bidang dapat diakses secara langsung atau melalui metode tidak benar-benar penting.

Invarian kelas (yang berguna) penting. Dan untuk melestarikannya, kita kadang-kadang tidak perlu bisa mengubah sesuatu dari luar. Misalnya. jika kita memiliki class Square dengan lebar dan tinggi yang terpisah, mengubah salah satunya menjadi sesuatu yang lain daripada persegi. Jadi kita perlu metode changeSide. Jika itu adalah Rectangle, kita bisa memiliki setter / bidang publik. Tapi setter daripada akan menguji apakah lebih besar dari nol akan lebih baik.

Dan dalam bahasa konkret (mis. Java) adalah alasan mengapa kita membutuhkan metode tersebut (antarmuka). Dan alasan lain untuk metode ini adalah kompatibilitas (sumber dan biner). Jadi lebih mudah untuk menambahkannya kemudian pikirkan apakah bidang publik sudah cukup.

btw. Saya suka menggunakan kelas holding nilai abadi yang tidak berubah dengan bidang final publik.

pengguna470365
sumber
Ini adalah satu-satunya hal yang harus dihindari. Beralih dari bidang ke properti / pengambil / penyetel merupakan perubahan besar dalam sebagian besar bahasa.
MrDosu
2

Anda mungkin ingin mengubah internal Anda ke apa pun sambil menjaga antarmuka yang sama. Jika antarmuka Anda tidak bervariasi, mereka yang Anda kode tidak akan rusak. Anda masih dapat mengubah internal Anda seperti yang Anda inginkan.

George Silva
sumber
1
Saya terkejut jawaban ini sangat jauh ke bawah. Getters dan setter memungkinkan Anda melakukan lebih banyak di masa mendatang (mis. Memecat suatu peristiwa, melakukan lebih banyak validasi input, melakukan pembukuan internal) tanpa melanggar API Anda yang ada. Bahkan dengan kode non-publik, semakin sedikit API yang harus Anda ubah, semakin sedikit ketergantungan untuk memperbarui, dan semakin sedikit bug yang diperkenalkan untuk membuat pembaruan tersebut.
AmadeusDrZaius
0

Pendekatan saya adalah ini -

Ketika saya berharap untuk menipu dengan data nanti, pengambil / penyetel dibenarkan. Juga, jika perubahan terjadi, saya sering mendorong data menjadi pengambil / penyetel.

Jika struktur POD, saya membiarkan slot tersedia.

Pada tingkat yang lebih abstrak, pertanyaannya adalah "siapa yang mengelola data", dan itu tergantung pada proyek.

Paul Nathan
sumber
0

Jika menggunakan getter dan setter terasa rumit, masalahnya mungkin bahasa, bukan konsep itu sendiri.

Berikut kode dari contoh kedua yang ditulis dalam Ruby:

class Fridge
  attr_accessor :cheese
end

def go_shopping fridge
  fridge.cheese += 5
end

Perhatikan itu terlihat sangat mirip dengan contoh pertama di Jawa? Ketika getter dan setter diperlakukan sebagai warga negara kelas satu, mereka bukan tugas untuk digunakan, dan fleksibilitas tambahan kadang-kadang bisa menjadi keuntungan nyata - misalnya, kita bisa memutuskan untuk mengembalikan nilai default untuk keju di lemari es baru:

class Fridge
  attr_accessor :cheese

  def cheese
    @cheese || 0
  end
end

Tentu saja akan ada banyak variabel yang seharusnya tidak diekspos secara publik sama sekali. Mengekspos variabel internal yang tidak perlu akan membuat kode Anda lebih buruk, tetapi Anda tidak bisa menyalahkan itu pada getter dan setter.

Tobias Cohen
sumber
Itu dinyatakan dengan indah. Fungsi pembuat ditambahkan karena kami ingin membuat objek yang tidak dapat diubah sementara secara bertahap membangun resep: mungkin jika Anda menambahkan pemanis buatan, Anda tidak perlu menambahkan gula. Menggunakan parameter bernama, dan bahkan dengan kelebihan beban, sangat sulit membangun satu metode melakukan semua. Dan tentu saja java tidak memiliki parameter bernama, maka satu lagi alasan orang menggunakan pola Builder.
YoYo
0

Pertimbangkan Sizekelas yang merangkum lebar dan tinggi. Saya bisa menghilangkan setter dengan menggunakan konstruktor tetapi bagaimana itu membantu saya menggambar persegi panjang dengan Size? Lebar dan tinggi bukan data internal ke kelas; mereka berbagi data yang harus tersedia untuk konsumen Ukuran.

Objek terdiri dari perilaku dan keadaan - atau atribut. Jika tidak ada keadaan terbuka maka hanya perilaku yang bersifat publik.

Tanpa status, bagaimana Anda menyortir koleksi benda? Bagaimana Anda mencari contoh objek tertentu? Jika Anda hanya menggunakan konstruktor, apa yang terjadi ketika objek Anda memiliki daftar atribut yang panjang?

Tidak ada parameter metode yang boleh dikonsumsi tanpa validasi. Karena itu malas menulis:

setMyField(int myField){
    this.myField = myField;
}

Jika Anda menulisnya dengan cara itu maka Anda setidaknya sudah siap untuk menggunakan setter untuk validasi; itu lebih baik daripada bidang publik - tetapi nyaris. Tetapi setidaknya Anda memiliki antarmuka publik yang konsisten sehingga Anda dapat kembali dan memasukkan aturan validasi tanpa melanggar kode pelanggan Anda.

Getters, setter, properti, mutator, sebut mereka apa yang Anda mau, tetapi mereka perlu.

Dale
sumber
Tetapi setidaknya Anda memiliki antarmuka publik yang konsisten sehingga Anda dapat kembali dan memasukkan aturan validasi tanpa melanggar kode pelanggan Anda. Itu tidak benar. Menambahkan aturan validasi lebih jauh di jalan bisa "melanggar kode pelanggan Anda". Ambil intvariabel instan, misalnya, dan bayangkan Anda memutuskan untuk menambahkan aturan validasi untuk hanya menerima nilai-nilai yang tidak negatif. Masalahnya adalah bahwa kode pelanggan Anda mungkin sudah bergantung pada kemungkinan untuk menetapkannya ke nilai negatif ...
jub0bs
0

Jika getter dan seter melanggar enkapsulasi dan OO sejati, maka saya serius dalam masalah.

Saya selalu merasa objek mewakili apa pun yang terlintas dalam pikiran bahwa Anda perlu melakukannya.

Saya baru saja selesai menulis sebuah program yang menghasilkan Maze di Jawa, dan saya memiliki kelas yang mewakili "Kotak Maze". Saya memiliki data di kelas ini yang mewakili koordinat, dinding, dan boolean, dll.

Saya harus memiliki beberapa cara untuk mengubah / memanipulasi / mengakses data ini! Apa yang saya lakukan tanpa getter dan setter? Java tidak menggunakan properti dan mengatur semua data saya yang bersifat lokal untuk kelas ini ke publik adalah PASTI pelanggaran enkapsulasi dan OO.

Bryan Harrington
sumber
6
Inilah pertanyaannya: Jika Anda menjadikan semua bidang itu publik, dan berhenti menggunakan getter dan setter bagaimana kode Anda akan berbeda?
Winston Ewert
Secara teori, ini bukan. Mengapa bahkan membuat kelas pada saat itu? Argumen yang sama bisa dibuat dengan apa saja. Tetapi pos di bawah milik saya tampaknya telah mengatakannya dengan lebih elegan. (Getters dan setters adalah cara untuk memberikan nilai ke suatu objek dengan aman, atau mengambil nilai dari objek tersebut.) Posting berlanjut ..
Bryan Harrington
3
Saya akan menunjukkan: poster itu akhirnya setuju dengan saya dan memposting jawaban lain untuk efek itu. Pada dasarnya, menggunakan getter dan setter sama dengan memanipulasi data secara langsung. Anda tidak benar-benar mendapatkan OOness dengan melakukan itu. Untuk menjadi OO Anda harus menyediakan antarmuka tingkat yang lebih tinggi untuk data.
Winston Ewert
4
Saya pikir cara terbaik untuk menggambarkannya adalah dengan mengatakan bahwa Anda harus merancang antarmuka Anda dari dalam ke luar, bukan ke dalam ke luar. Antarmuka yang disediakan oleh objek Anda harus ditentukan oleh bagaimana objek tersebut digunakan, bukan bagaimana implementasinya. Jadi jangan menulis getter dan mengizinkan objek eksternal untuk menginterpretasikan data. Cari tahu pertanyaan apa yang perlu dijawab, dan tulis metode yang menjawab pertanyaan itu. Jangan izinkan objek eksternal mengubah keadaan oleh setter, cari tahu manipulasi apa yang mereka butuhkan untuk melakukan dan menulis metode yang melakukan itu.
Winston Ewert
2
Dalam beberapa kasus, Getters and Setters adalah metode terbaik. Terkadang mereka adalah antarmuka terbaik yang dapat Anda berikan. Namun, dalam banyak kasus tidak demikian. Dalam banyak kasus, Anda hanya membocorkan detail implementasi ke kelas lain.
Winston Ewert
-1

Pertama ada dua jenis objek yang akan saya komentari, nilai dan jenis layanan.

Jenis layanan tidak boleh memiliki setter, dependensi apa pun yang mereka butuhkan tidak dapat diperoleh. Cara terbaik untuk melewati dependensi adalah melalui konstruktor atau pabrik dengan cara itu semua instance sepenuhnya terbentuk dari awal, sederhana dan sederhana.

Jenis nilai juga harus tidak berubah, di sisi lain ada saat-saat ini tidak praktis seperti ORM, atau beberapa pemetaan lain seperti dari widget ke objek. Semua tipe nilai lain yang dapat melewati sistem dari satu lapisan atau bagian ke lapisan lain harus tidak berubah dan tidak boleh memiliki setter.

mP01
sumber
Saya menyarankan jenis ketiga: wadah yang bisa berubah, yang tujuannya adalah untuk memegang satu atau lebih nilai; dalam banyak kasus, sebuah wadah seharusnya tidak diharapkan memiliki banyak cara logika atau validasi. Kode yang memerlukan metode untuk menghitung enam bilangan bulat positif dapat meneruskan wadah untuk enam bilangan bulat ke metode yang akan menyimpan hasil di dalamnya. Tanggung jawab untuk memastikan angka-angka positif umumnya harus berada pada penelepon atau metode yang dipanggil, bukan dengan wadah.
supercat
-1

Getter / Setter dibenarkan sama seperti metode publik lainnya dibenarkan. Pembenaran ini terutama karena kami ingin menyediakan antarmuka ke dunia luar yang mendefinisikan bagaimana kelas kami berinteraksi dengan kelas-kelas lain. Kami melakukan ini terutama karena kami ingin mengurangi sambungan antara entitas yang terpisah. Mengurangi sambungan adalah hal yang baik karena berbagai alasan:

  • Saya dapat menggunakan dari kode saya saat ini antarmuka kelas yang sama meskipun sementara itu kelas menambahkan metode baru (tetapi mempertahankan antarmuka lama)
  • Dapat mengubah representasi internal kelas tanpa mempengaruhi konsumennya
  • Kurangi kemungkinan bug: jika Anda menjadikan data internal Anda pribadi, Anda dapat mengetahui dengan pasti bahwa kode eksternal tidak akan mengacaukan data internal Anda
Ghita
sumber
-1

Ya, getter dan setter adalah anti-pola dalam pemrograman berorientasi objek dan tidak boleh digunakan: http://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html . Singkatnya, mereka tidak cocok dengan paradigma berorientasi objek karena mereka mendorong Anda untuk memperlakukan objek seperti struktur data. Yang merupakan kesalahpahaman besar.

yegor256
sumber
2
ini tampaknya tidak menawarkan sesuatu yang substansial atas poin yang dibuat dan dijelaskan dalam 18 jawaban sebelumnya
agas
4
Ini juga agak kuat untuk mengatakan tidak pernah .
Winston Ewert
-4

Saya berharap bahwa apa pun yang dikembangkan oleh perusahaan IDE yang mengembangkan bahasa pemrograman Anda secara otomatis tidak melanggar "Prinsip Orientasi Objek".

Yang sedang berkata, setiap kali Anda memiliki variabel cakupan publik, gunakan pengambil dan penyetel dan Anda emas. Menurut saya elemen yang diperlukan dari prinsip enkapsulasi yang sangat berorientasi objek - bahkan jika itu terlihat seperti banyak kode barang rongsokan.

Alasan untuk menggunakannya adalah bahwa enkapsulasi yang tepat mungkin belum terjadi dan selama Anda telah mengabstraksi lapisan yang memungkinkan orang yang sedang bermain dengan objek Anda merasakan objek Anda, Anda dapat mengambil objek itu tanpa dia menyadarinya kan?

Cukuplah untuk mengatakan, sebuah rumah yang terbagi tidak dapat berdiri, salah satu penyewa OO tidak akan menyalakan dirinya sendiri dan Anda tidak dapat melayani enkapsulasi dan optimasi prematur.

Peter Turner
sumber
4
dan apa sebenarnya yang saya peroleh dengan menambahkan banyak kode untuk memanggil setFoo () dan getFoo () setiap kali saya berurusan dengan variabel?
Winston Ewert
7
Ini masalahnya, saya tidak berpikir Anda mendapatkan enkapsulasi. Anda masih memanipulasi keadaan objek dari tempat lain. Anda hanya melakukannya dengan lebih tegas. Untuk mendapatkan enkapsulasi, Anda perlu memusatkan manipulasi nilai di kelas yang memiliki nilai itu. Yang lainnya hanyalah kode prosedural dalam pakaian OO. (Tidak ada yang salah dengan itu, tetapi ini adalah apa adanya).
Winston Ewert
2
Keuntungan enkapsulasi adalah bahwa semua logika yang terkait dengan nilai-nilai tertentu ada di satu tempat (lebih atau kurang). Saya tidak mengerti dengan getter dan setter. Karenanya, saya pikir saya tidak mendapatkan banyak manfaat untuk mereka.
Winston Ewert
1
Namun, saya akan setuju bahwa saya lebih suka memiliki kode dengan getter dan setter daripada yang memanipulasi data secara bebas melalui akses publik. Dengan begitu saya setidaknya bisa melampirkan validasi data yang masuk.
Winston Ewert
3
Saya tentu bukan penggemar kata-kata. Tetapi keberatan saya adalah orang-orang yang membuat variabel mereka pribadi dan kemudian memanggil getter dan setters pada mereka dengan bebas berakhir dengan tidak banyak perbedaan. Jika Anda ingin menggunakan getter / setter dalam kelas Anda, itu tidak masalah. Pertanyaannya adalah mengapa? Manfaat utama yang saya tahu ada adalah bahwa Anda dapat melakukan beberapa validasi pada data untuk menangkap kesalahan sebelumnya. Ini kurang membantu ketika semua kode yang memanipulasi data ada di tempat yang sama. Tapi itu masih bisa membantu. Idealnya, Anda memiliki bahasa yang mendukung properti dan dapat menghindari verbositas.
Winston Ewert
-4

Saya pikir kita akan memiliki jawaban yang lebih biasa di sini?

Getters dan setters pada umumnya sangat OOP (ada yang mengatakan hal lain salah, sesederhana itu).

Meskipun Anda harus ingat untuk tidak over engineer, jangan pernah membuat rajin atau rajin yang tidak diperlukan. Informasi apa pun yang tidak akan Anda gunakan oleh kelas lain Anda tidak boleh memiliki pengambil atau penyetel untuk.

Padahal, alasan mengapa Anda tidak membuat bidang publik dan sebaliknya memiliki getter dan setter kebanyakan:

  • Tes yang tepat tidak mungkin dilakukan dengan menggunakan bidang. Getters / setter jauh lebih baik dalam hal itu.

  • Tidak mungkin menggunakan bidang dengan benar dalam pengaturan pemrograman waktu nyata. Getter / setter yang disinkronkan adalah cara untuk pergi.

  • Topik antarmuka (sudah dijelaskan jadi saya tidak akan).

  • Lebih mudah digunakan ketika menggunakan kembali sistem dengan benar.

  • Jauh lebih sulit untuk mengetahui kelas mana yang menggunakan kelas Anda dan apa yang akan pecah jika Anda mengubah bidang daripada pengambil / penyetel.

Untuk itu satu-satunya saat Anda menggunakan bidang publik alih-alih pengambil / penyetel adalah ketika Anda tidak membuat proyek resmi, melainkan hanya melakukannya untuk bersenang-senang dan tidak dapat repot-repot dengan pengambil / penyetel.

Skarion
sumber
2
+1 untuk testabilitas. Terkejut bahwa tidak ada yang menyebutkannya sebelumnya. Fitur berguna lainnya dari properti adalah men-debug-nya. (jauh lebih mudah untuk memasukkan breakpoint dalam kode daripada membutuhkan hardware / memory breakpoints untuk bidang).
Mark H
9
Keberatan getter dan setters adalah bahwa mereka sering digunakan untuk mengikuti surat hukum OOP sambil mengabaikan semangat dan niat. OOP mengatakan bahwa Anda seharusnya tidak membiarkan benda lain bercampur dengan internal Anda. Jika Anda mendefinisikan getter dan setter yang memungkinkan Anda melakukan itu, Anda akhirnya melanggar OOP.
Winston Ewert
6
Saya akan setuju bahwa ada banyak manfaat getter dan setter di bidang publik yang sederhana. Namun, saya tidak jelas bagaimana pengujian lebih baik dengan mereka. Bisakah Anda jelaskan?
Winston Ewert
4
@ Skarion: Saya tidak setuju dengan jawaban Anda. Anda tampaknya menghadirkan dua alternatif: baik menggunakan getter / setter atau membuat bidang publik. Tetapi ada opsi ketiga: tidak menggunakan getter / seter atau bidang mengekspos! Mengapa Anda perlu menguji keadaan bidang internal? Bukankah Anda seharusnya menguji API publik objek Anda?
Andres F.