it-swarm-id.com

Kapan Getters and Setters dibenarkan

Getters dan setters sering dikritik sebagai OO yang tidak tepat. Di sisi lain kebanyakan OO kode 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 sebagai pengambil dan penentu untuk pertanyaan ini. Mereka adalah hal yang sama dari perspektif metodologi OO. Mereka hanya memiliki sintaksis 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:

179
Winston Ewert

Memiliki getter dan setter tidak dengan sendirinya merusak enkapsulasi. Apa yang dimaksud break encapsulation adalah secara otomatis menambahkan pengambil dan penyetel untuk setiap anggota data (setiap bidang, dalam Java istilah), tanpa memikirkannya. lebih baik daripada membuat semua anggota data terbuka, hanya tinggal selangkah lagi.

Maksud enkapsulasi bukanlah bahwa Anda seharusnya tidak dapat mengetahui atau mengubah keadaan objek dari luar objek, tetapi bahwa Anda harus memiliki kebijakan wajar 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(), daripada setter.

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

Pertimbangkan contoh 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 oleh 1. Anda juga akan menyediakan 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 satu tahun dalam satu waktu.

Sekarang katakanlah seseorang juga memiliki gaji. Dan orang dapat berganti pekerjaan sesuka hati, yang berarti gajinya juga akan berubah. Untuk memodelkan situasi ini, kami tidak memiliki cara lain selain menyediakan metode setSalary()! Membiarkan gaji diubah sesuka hati adalah kebijakan yang sangat masuk akal dalam kasus ini.

By the way, dalam contoh Anda, saya akan memberikan kelas Fridge the putCheese() dan takeCheese() metode, alih-alih fungsi 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() )
  }
}
163
Dima

Alasan dasar untuk getter dan setter di Java sangat sederhana:

  • Anda hanya dapat 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.

42
user1249

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 mengkode 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.

20
Robert Harvey

Kapan getter dan setter dibenarkan?

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

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

Edit

Jawaban itu mungkin dianggap remeh, jadi izinkan saya memperluas. Jawaban di atas sebagian besar benar tetapi fokus pada paradigma pemrograman OO desain dan beberapa yang melewatkan gambaran besar. Dalam pengalaman saya ini membuat orang berpikir bahwa menghindari pengambilan dan setter adalah beberapa aturan akademik untuk OO bahasa pemrograman (mis. Orang berpikir mengganti getter dengan properti 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 yang baik atau tidak OO pemrograman. Satu-satunya titik yang pada akhirnya yang penting adalah bahwa jika tidak ada apa pun di domain Anda yang berfungsi seperti ini dengan meletakkannya di Anda, bukan membuat model 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 cukup mengatakan "Tetapkan gaji ini ke X" atau "Dapatkan 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.

14
Cormac Mulhall

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 membuatnya 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 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 yang mungkin Anda miliki kemudian ganti bidang yang tersimpan dengan perhitungan on the fly tanpa merusak antarmuka, tetapi ini adalah pengecualian.

11
rwallace

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 membuatnya 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.

3
user470365

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.

2
George Silva

Pertimbangkan kelas Size yang merangkum lebar dan tinggi. Saya dapat 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; ini 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.

0
Dale

Pendekatan saya adalah ini -

Ketika saya berharap untuk menipu dengan data nanti, pengambil/penyetel dibenarkan. Juga, jika perubahan sedang 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.

0
Paul Nathan

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.

0
Bryan Harrington

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

Ini kode dari contoh kedua yang ditulis dalam Ruby:

class Fridge
  attr_accessor :cheese
end

def go_shopping fridge
  fridge.cheese += 5
end

Perhatikan itu sangat mirip dengan contoh pertama di Java? 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 dapat 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.

0
Tobias Cohen