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 Fridge
yang 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() )
}
}
Getters and setters are often criticized as being not proper OO
- Tolong kutip.