「過分」地追求 OOP 有意義嗎?

比如在Person的實例jack里有一個叫age的data member

我們老師在給我們出題目的時候會把

age = a ;

寫成

jack.setAge(a) ;

b = age ;

寫成

b = jack.getAge() ;

請問他這樣寫只是為了訓練我們使用OOP還是有什麼其他好處?


『過分』「地」追求什麼是有用的呢?

你想那麼多沒用,告訴你也沒用,自己得出結論才重要。

就算我現在告訴你了,你也相信了,有意義嗎?

7 歲的孩童,你告訴他人生真理,他能懂嗎?

回到你的問題上來,其實非常好解決,你把你們學校的段位排一下,把老師的能力套入學校的段位就可大致得出他所說言論的可信度(基本是這樣),所以無論如何這種事情你都要自己思考探索的。

至於你的具體問題,老師的寫法問題不大,就是品位低了點,如果是我的話,會這麼寫

b = j.age();
j.setAge(a);

在一些現代化的語言像是 Python 裡面,就集成了你的那個語法糖,你看不出來是個方法,還以為是屬性呢。。。


當然有意義。而且一點都不過分。只不過你沒一定代碼量無法體會,所以會覺得毫無意義。

譬如,當出現了一個與age相關的bug的時候,如果到處都是age=xxx,你就得打n多斷點在這些地方。

而有了set之後,你只需要在set處打斷點就行了。

=============

比較不爽的是好多人在噴他老師的寫法,我真是。。。。

心中一萬頭草泥馬奔過。

就好像別人問為啥我要早上喝牛奶?

他卻在批評純牛奶不好喝。我們來喝酸奶吧。牛頭不對馬嘴。

喝牛奶當然是為了強身健體長得更高啊。


沒有,但這跟OOP有什麼關係?


這裡 getXxx 、 setXxx 是 Java 社區的慣例,最大的好處是在修改實現時不破壞向後兼容性。參見 Java bean 是個什麼概念? - 楊博的回答

在你開發可重用的庫或框架時,如果想要保證你的庫或框架能被團隊外的人重用,你就得向他們承諾向後兼容性。

為了達到向後兼容性,你需要遵守很多準則(比如如何用final、如何用public/protected/private),被迫把Java代碼寫得很冗長。軟體框架設計的藝術介紹了這些準則。


正常情況下age這種應該設置成私有成員,不可能讓你直接就隨便訪問的,要更改必須通過公有成員函數來進行。

小程序沒必要這麼搞,但如果你要造大一點的輪子,給別的人用的話就得封裝起來了


我來唱唱反調吧。

第一點就是,所謂的 getter/setter 在 C++ 里完全可以使用宏來實現。

第二點就是,很多人強調的「現在是 trivial 的 getter/setter,以後就會變成 non-trivial 的」,我很好奇真的有這種事情發生么。。

再比如說,如果我更改了存儲年齡為存儲出生日期,getAge方法可以繼續使用,而.age肯定沒法用了。

你都改成出生日期了,還叫 getAge?難道不應該叫做 getBirthday?

對於簡單項目,當然可以不封裝setAge,但是封裝了setAge也並非「過分」。

如果只有 getter 或者只有 setter 當然不過分,getter 和 setter 本身是 non-trivial 的也不過分,但是 trivial getter setter 滿天飛,的確過分了。

這個問題我在 StackOverflow oop - Why use getters and setters? 搜索過,摘抄一個人的評論如下:

because getters and setters buy you next to nothing over public data ("It is hard for me to imagine an evolution of a system that would let you keep the interface of get and set, but be able to change the implementation."Alexander Stepanov) and promote a style that leads to "quasi classes" (Conrad Weisert), which are an abomination onto OO.– sbi

另外,當你的某個類全部都是 trivial getter/setter 時,應該考慮 struct。


在教面向對象時當然有意義。

在面向對象對象編程中,你關心的是對象暴露出的行為特性,而非內部實現,這樣,對象內部實現和你才能解耦。

比如說,我想在年齡變化時刷新界面顯示的年齡,你怎麼做?就是重載setAge方法,觸發屬性變更事件。

再比如說,如果我更改了存儲年齡為存儲出生日期,getAge方法可以繼續使用,而.age肯定沒法用了。

在面向對象方法中,你只是給對象發了一個更改年齡的消息,怎麼更改是對象響應這個消息的事,比如可能驗證下年齡是不是負數啊,你是無法越俎代庖的。


我不知道你注意沒注意,effective c#第一個item就是「使用屬性而不是可訪問的數據成員」。道理不多說了,自己看書。

也許是java的語法繁雜導致你懷疑這條規則,但是繁雜是java的坑,這條規則始終是對的。


養成好習慣,雖然說強行OOP並不是什麼好習慣……

但總之規範化一點並不是特別壞。OOP就像是一套行之有效的官僚系統,最好的狀態當然是不要官僚了,但問題複雜起來以後呢


我初學Java時也納悶,直到有一天,我想獲取一個字元串,不可以是null,如果是,就把它寫成""。這個時候我就可以用getter處理,不必取出來以後判斷了。同樣的例子還有取一個int,如果是負數就取絕對值,我還是在getter裡面做,不用取出來後再判斷。

而且用getter/setter方法,容易追蹤代碼,很容易知道某個變數在什麼時候被改變了,方便定位問題點。

同時,如果一個數據,我可能希望是只讀,或者是只寫的,直接訪問屬性是實現不了的,這時可以單獨提供getter或者setter,就可以了。


其實沒啥意義,你看看敏捷宣言,沒有提到面向對象。


你們老師那個是java寫多了吧,getXXX,setXXX是java的約定

不過好的代碼風格對於寫出簡單易讀的代碼很有幫助

但是java這語法能寫出啥「簡單易讀」

C++

class Person
{
private:
int _age;
public:
int Age() { return _age; }
void Age(int value) { _age = value; }
};

C#就一行

class Person
{
public int Age {get; set;}
}


這是一個"封裝(encapsulation )"意識。既然你有這樣的疑問,那你就要思考一下你有沒有理解OO思想中的屬性的訪問許可權,public protected private。如你所說,確實可以直接把age屬性設置成public然後直接訪問,但是嚴格來說,一個person的age只能由某些模塊去讀寫。所以你老師的做法沒什麼不妥。


這就叫「過分」追求OOP了?


記得之前有人回答過OOP的一個問題,總結就是「你辦事我放心」,我舉得非常精闢

一件事(比如setAge),你要關心的只是其介面,而無需關心其內部實現,現在也許只是age = a,但是未來也許會轉成UnixTimestamp再存,也許會額外保存在一個全局的登記表中,而這些都是你不需要關心的。

面向對象更多的是一種思考問題的方式,沒有虛函數和繼承,我們仍然可以用C寫出易用易維護的面向對象代碼(比如glib,注意是glib不是glibc),而對於實際編碼,則可以根據項目複雜度做些折中考慮。對於簡單項目,當然可以不封裝setAge,但是封裝了setAge也並非「過分」。


可以看看軟體工程這本書,先看結構化設計再看面向對象設計。

我去複習去了(逃


分頁阅读: 1 2 3