拥有getter和setter方法本身并不会破坏封装。破坏封装的方法是自动为每个数据成员(Java术语中的每个字段)添加一个getter和setter ,而无需考虑任何问题。尽管这比使所有数据成员公开更好,但仅一步之遥。
封装的重点不是您不应该从对象外部知道或更改对象的状态,而是应该拥有合理的策略来做到这一点。
一些数据成员可能完全在对象内部,并且既不应该具有getter也不应该具有setter。
一些数据成员应该是只读的,因此它们可能需要getter但不需要setter。
某些数据成员可能需要保持彼此一致。在这种情况下,您将不会为每个设置程序提供设置程序,而是提供一种同时设置它们的单一方法,以便您可以检查值的一致性。
某些数据成员可能仅需要以某种方式进行更改,例如以固定量递增或递减。在这种情况下,您将提供一个increment()
和/或decrement()
方法,而不是一个setter。
还有一些实际上可能需要读写,并且同时具有getter和setter。
考虑一个例子class Person
。假设一个人有一个名字,一个社会保险号和一个年龄。假设我们不允许人们更改其姓名或社会保险号。但是,该人的年龄每年应增加1。在这种情况下,您将提供一个构造函数,该构造函数会将名称和SSN初始化为给定值,并将年龄初始化为0。您还将提供一个方法incrementAge()
,该方法会将年龄增加1。所有三个的吸气剂。在这种情况下,不需要二传手。
在此设计中,您允许从类外部检查对象的状态,并允许从类外部更改对象的状态。但是,您不允许随意更改状态。有一项策略有效地声明了名称和SSN完全不能更改,并且年龄可以一次增加1年。
现在,假设一个人也有薪水。人们可以随意更换工作,这意味着他们的工资也将改变。为了模拟这种情况,我们只能提供一种setSalary()
方法!在这种情况下,允许随意更改薪水是一个完全合理的政策。
顺便说一句,在你的榜样,我会给类Fridge
的putCheese()
和takeCheese()
方法,而不是get_cheese()
和set_cheese()
。这样您将仍然具有封装。
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
-请引用。