2013年1月9日 星期三

The evil getter and setter 萬惡的getter和setter


今天在Stackoverflow上讀到了一篇文章 Are Getters and Setters evil?  (getter跟setter真的那麼邪惡嗎?)

把一些重點摘錄如下,有習慣看英文的可以直接去看這邊: http://stackoverflow.com/questions/565095/are-getters-and-setters-evil

其中一個大原則是,針對private field, 不使用public setter去直接改值, 用其他有意義且必要的metohd去改變值

範例:  使用kill()或者是damaget()這兩個method去對private的hp欄位進行值的更改,而不使用setHp()來修改
(發表者Zarkonnen)
private int hp; // Set in constructor.
public boolean isAlive() { return hp > 0; } // Same method signature.
public void kill() { hp = 0; } // Same method signature.
public void damage(int damage) { hp -= damage; }


而另一位Jon Skeet網友則如此定義邪惡的等級 (或者說是code的好壞/安全性/OO觀念):

Very evil: public fields.
Somewhat evil: Getters and setters where they're not required.
Good: Getters and setters only where they're really required - make the type expose "larger" behaviour which happens to use its state, rather than just treating the type as a repository of state to be manipulated by other types.
不確定我是否理解正確,他覺得最糟的狀況是直接使用public field (比不必要的getter/setter還糟),好的狀況則是在必要的狀況下使用getter及setter,而所謂必要的狀況,應該是有一些額外的行為想要在getter及setter裡面做,而非僅僅取值/設值.

那除了取值/設值外,會有什麼"額外的行為"是需要在getter和setter裡面做的呢?
 coobird網友提供了兩個例子:
範例1: 計算值遭到修改的次數   (更常見的狀況可能是驗證使用者所輸入的資料)
 public void setValue(int value)
{
    this.value = value;
    count++;
}
範例2: 保護原值不任意遭到修改
public int[] getArray()
{
    return myArray.clone();
}
ImmutableArray a = new ImmutableArray();
int[] b = a.getArray();
b[0] = 10;      // 不會真正更改到myArray本身

而也有人提出 (網友nathan),如果在setter裡面做了許多多餘的動作,別人在使用此API時並無法得知, 如果每個setter使用者都要去確認相當麻煩。
像是這樣的method會是個壞例子

public void setAlive(boolean isAlive){
    this.isAlive = isAlive;
    if (isAlive)
        addToWorld(this);
    else
        removeFromWorld(this);
}
建議改成在setter外的method做,如:
public void kill(){
    isAlive = false;
    removeFromWorld(this);
}

還有一篇裡面有人引用到的來自Java World的文章,作者為Allen Holub,還沒讀完,等讀完再一併整理...  (天啊..看日期是九年前發表的文章的意思嗎...)
http://www.javaworld.com/javaworld/jw-01-2004/jw-0102-toolbox.html