Có getters và setters không tự nó phá vỡ đóng gói. Những gì không đóng gói đóng gói là tự động thêm một getter và setter cho mọi thành viên dữ liệu (mọi trường , trong biệt ngữ java), mà không cần suy nghĩ gì. Trong khi điều này tốt hơn là làm cho tất cả các thành viên dữ liệu được công khai, nó chỉ là một bước nhỏ.
Điểm đóng gói không phải là bạn không thể biết hoặc thay đổi trạng thái của đối tượng từ bên ngoài đối tượng, mà là bạn nên có chính sách hợp lý để thực hiện.
Một số thành viên dữ liệu có thể hoàn toàn bên trong đối tượng và không nên có getters hay setters.
Một số thành viên dữ liệu nên ở chế độ chỉ đọc, vì vậy họ có thể cần getters nhưng không phải setters.
Một số thành viên dữ liệu có thể cần phải được giữ phù hợp với nhau. Trong trường hợp như vậy, bạn sẽ không cung cấp một setter cho mỗi cái, nhưng một phương thức duy nhất để đặt chúng cùng một lúc, để bạn có thể kiểm tra tính nhất quán của các giá trị.
Một số thành viên dữ liệu có thể chỉ cần được thay đổi theo một cách nhất định, chẳng hạn như tăng hoặc giảm theo một số tiền cố định. Trong trường hợp này, bạn sẽ cung cấp một increment()
và / hoặc decrement()
phương thức, thay vì setter.
Tuy nhiên, những người khác thực sự có thể cần phải đọc-ghi, và sẽ có cả getter và setter.
Hãy xem xét một ví dụ về a class Person
. Giả sử một người có tên, số an sinh xã hội và tuổi. Hãy nói rằng chúng tôi không cho phép mọi người thay đổi tên hoặc số an sinh xã hội. Tuy nhiên, tuổi của người đó nên được tăng thêm 1 mỗi năm. Trong trường hợp này, bạn sẽ cung cấp một hàm tạo khởi tạo tên và SSN cho các giá trị đã cho và sẽ khởi tạo tuổi thành 0. Bạn cũng sẽ cung cấp một phương thức incrementAge()
, sẽ tăng tuổi lên 1. Bạn cũng sẽ cung cấp getters cho cả ba. Không có setters được yêu cầu trong trường hợp này.
Trong thiết kế này, bạn cho phép trạng thái của đối tượng được kiểm tra từ bên ngoài lớp và bạn cho phép nó được thay đổi từ bên ngoài lớp. Tuy nhiên, bạn không cho phép thay đổi trạng thái tùy ý. Có một chính sách, trong đó nêu rõ rằng tên và SSN hoàn toàn không thể thay đổi và tuổi có thể tăng thêm 1 năm một lần.
Bây giờ hãy nói rằng một người cũng có một mức lương. Và mọi người có thể thay đổi công việc theo ý muốn, có nghĩa là tiền lương của họ cũng sẽ thay đổi. Để mô hình hóa tình huống này, chúng tôi không có cách nào khác ngoài việc cung cấp một setSalary()
phương pháp! Cho phép mức lương được thay đổi theo ý muốn là một chính sách hoàn toàn hợp lý trong trường hợp này.
Bằng cách này, trong ví dụ của bạn, tôi sẽ cung cấp cho lớp Fridge
các putCheese()
và takeCheese()
phương pháp, thay vì get_cheese()
và set_cheese()
. Sau đó, bạn vẫn sẽ đóng gói.
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
- Xin trích dẫn.