我正試圖圍繞面向對象的編程進行包裝。面向對象編程 - 類設計混淆
我的理解是,我們有對象,所以我們可以設計我們的程序來鏡像真實的對象。
讓我們的類層次:
很明顯,你可以用水果多態,如果Eat()
是虛擬的。但是這有意義嗎?水果不能自己吃!
水果物體應該傳遞給具有Eat()
函數的人類物體嗎?
我想弄清楚這個問題的正確方法。一般來說,編程物體應該如何反映真實物體?
我正試圖圍繞面向對象的編程進行包裝。面向對象編程 - 類設計混淆
我的理解是,我們有對象,所以我們可以設計我們的程序來鏡像真實的對象。
讓我們的類層次:
很明顯,你可以用水果多態,如果Eat()
是虛擬的。但是這有意義嗎?水果不能自己吃!
水果物體應該傳遞給具有Eat()
函數的人類物體嗎?
我想弄清楚這個問題的正確方法。一般來說,編程物體應該如何反映真實物體?
你有一個設計問題 - 正如你正確指出的那樣,Eat()作爲Fruit的一員並不明顯。另一方面,「食用」屬性會更有意義。就像「onEaten」事件等等。你的水果/蘋果類暴露的內容(以及你的模型中的其他對象是否有意義)取決於許多其他因素,包括你試圖用這些結構在你的申請。
一般而言,您希望您的類表示邏輯域級別實體。有時那些對應於現實世界中的物理實體,但是在許多情況下它們沒有。
在我看來,面向對象問題的分解是程序員通常很糟糕的地方。我不知道有多少次我看過相當於一輛由方向盤衍生出來的汽車,它搖動了我的頭,而原先的開發人員無法繞頭說明爲什麼他們的設計沒有多大意義。
如果一個水果對象而被傳遞到 具有吃()函數 人物對象?
是的。
但程序對象通常比這個簡單的例子更抽象。在計算機編程的現實世界中,像水果和人類這樣的對象通常會被表示爲數據庫中的屬性。這些數據的消耗和操作將在編程對象中完成。
你說得對,可能會吃()會是人或哺乳動物或果蠅的一種方法。水果本身可能沒有行爲,因此也沒有方法。
我不會經常考慮OO在實際中從真實事物到對象映射的好處。這可能是因爲我們處理的是較爲有形的概念,例如發票和訂單。
我看到OO的主要勝利在於它爲我的代碼帶來的結構。在現實世界發票和訂單實際上並不做什麼,但在我的代碼他們做。因此,程序化訂單可能更接近於表示訂單的數據和與訂單相關的一些人類業務過程的組合。
大多數人類語言遵循Subject Verb Object
句子結構(簡單的語句)在面向對象的語言,如C++,並不完全具有諷刺意味的,對象是對象,而且它是第一位的,所以正常順序被顛倒:
因此,這是C++中的 '基本' 模式: -
object.verb(subject);
這實際上是垃圾。大多數類都使用subject.verb(object)接口進行設計。但是SVO的知識確實允許測試類接口是否被設計爲正確的方法(在這種情況下,它沒有)。
也就是說,有大量的人類語言自然是OVS,或者其他一些典型的英語順序顛倒過來的變體。在處理國際開發的軟件時,其他開發人員可能會對主題的正確順序和正常順序以及簡單語句中的對象有不同的看法。
好像你在這種情況下倒退了,一般來說。 human.eat(蘋果)就是我們在這裏看到的。 Subject.verb(對象)。 – Grumdrig 2009-11-16 08:03:54
實際上,OSV和OVS語言似乎很少見。更多的支持將正式的SVO作爲OOP設計中的正確順序。 – 2009-11-16 09:54:44
假設你是寫一個飢餓的人模擬器,那麼我認爲這將更有意義,正如你所說,有一個Human::Eat(Fruit f)
函數。你的水果可能沒有方法,因爲水果本身並沒有多大作用,但它可能有一個calories
屬性,依此類推。
我傾向於認爲有關:
是一個
擁有
所以,蘋果是一種水果,所以繼承是有道理的。
但是,水果具有可食用性可能是有意義的,但這表明它是水果的屬性,而不是行爲(方法)。
例如,你可能有未成熟的蘋果,那是不可食用的(可食用的),所以你可以設置這個屬性。
現在,無論將要吃這個,你可以設置一個蘋果是否是它的飲食的一部分。
現在Has a
將用於組成。所以,一顆蘋果擁有一顆種子意味着種子不會延伸蘋果,但是蘋果會擁有一批種子。
所以,你確實有設計問題,我希望這兩個概念可能有助於澄清。
「Is-a」(繼承)和「has-a」(構圖)比喻是考慮事情的好開始,但值得注意的是這具有一些侷限性。最好是去選擇「比繼承更喜歡組合」,而不是認爲關於「是 - 並且具有 - 」的所有內容都是因爲後者導致了對繼承的偏好。維護大的繼承結構是很困難的。 – Spoike 2009-11-16 08:31:21
@Spoike - 當一個人是OOP的新手時,保持簡單是最好的選擇,我認爲這兩個概念有助於簡化它。一旦這些更好理解,那麼你可以看看其他方法來理解,國際海事組織。 – 2009-11-16 08:37:44
簡單地鏡像真實世界的對象並不是一個好主意。借用一個經典的例子 - 控制咖啡機的軟件不是關於咖啡豆和熱水 - 而是關於製作咖啡。
您需要找到您的現實世界問題的底層抽象,而不只是將名詞複製到對象層次結構中。
如果你的蘋果來自水果,它是否會添加任何有趣的行爲?層次真的需要嗎?繼承增加了軟件的複雜程度,任何增加複雜性的東西都是不好的。你的軟件只是有點難以遵循和理解,測試中只有更多的東西可以覆蓋,而且錯誤的可能性稍微大一點。
我發現OOP更多的是關於空白 - 你留下的是更重要的。
一般而言,編程對象應如何反映真實對象。
不多,只夠了。
面向對象的一個主要特徵是抽象。您不需要讓對象的所有屬性/方法都能夠使用它。
你只需要基本的使用它。
關於對象的全部事情是讓數據和函數在同一個地方執行關於該數據的一些事情。
所以在你的水果班上,我最好有Color
的東西或是否會被吃掉的跡象。例如:
Fruit
+ color : Color
- isMature : Boolean
+ canBeEaten() : Boolean
return isMature
這樣,您可以創建不同的水果
apple = Fruit()
appe.color = Color.red
out.print("Can this fruit be eaten? %s ", apple.canBeEaten())
orange = Fruit()
orage.color = Color.orange
out.print("Can this fruit be eaten? %s ", orange.canBeEaten())
等等
如果你看到的屬性(顏色和isMature)存儲在對象中。這樣你就不必從外面追蹤他們的狀態。
至於繼承,只有在需要爲某種方法添加新行爲時纔有意義,而且,方法與對象的屬性或特性有關。正如你指出fruit.eat()
沒有多大意義。
但是考慮一種從水果中得到果汁的方法。
Fruit
+ getJuice(): Juice
Apple -> Fruit
+ getJuice(): Juice
// do what ever is needed internally to create the juice
Orange -> Fruit
+ getJuice(): Juice
// here, certainly the way to get the juice will be different
我總是發現使用'動物'或'水果'違反直覺的例子。人們是難以模仿的對象,而且你會遇到具有相同需求的應用程序,這是不明智的。
使用擬人化概念確實可以幫助分配職責。基本上,想象你的物體是一個人(我通常在設計會議期間用一張臉和四肢來畫他們)。
現在你可以問你的對象這些問題:
大衛韋斯特的「對象思維」是一本關於該主題的好書。
類食草動物的某些東西會具有Eat功能,就像Carnivore類的東西一樣,但每個人的Eat對於可以傳遞給Eat功能的參數都會有一些不同的限制。水果是什麼被吃,所以它將作爲參數傳遞給Herbivore.Eat(),而你想要傳遞一個類型爲Hamburger的對象給Carnivore.Eat(),並且在漢堡傳遞時傳遞一個異常到Herbivore.Eat()。
但實際上,我不認爲OOP是如此,所以我們可以將軟件對象建模爲與真實對象一樣。我發現我的大部分OOP設計都是用很漂亮的抽象對象來工作的,只有在它們所屬的系統方面。如果我編寫了一個庫簽入/簽出系統,我將根據其管理屬性和函數對一本書進行建模 - 我不會將它建模爲Page對象的集合,我懷疑我甚至不會定義類似Read()方法的東西,儘管這首先是一本書的主要目的。 Book對象在系統中的角色決定了我的設計,遠遠超過了現實世界中的書籍。
很好的答案。 OOP中的命名類更多地是使用人類熟悉的概念。這樣就更容易假設代碼模塊的行爲。食肉動物可能根據正在建模的系統有不同的行爲,但無論如何,它比說ThingThatOnlyEatsMeat更直觀。 – TheSecretSquad 2013-06-19 00:03:39
我的理解是,我們有對象,所以我們可以設計我們的程序來鏡像真實的對象。
也許更像是將他們與真實生活對象「聯繫起來」,如果適用的話。
水果物體是否應該傳遞給具有Eat()函數的人類物體?
是的,或者比人類更普遍的東西。
我想弄清楚這個問題的正確方法。一般而言,編程對象應如何反映真實對象。
只定義您需要定義的內容。然後執行(通常)變得非常明顯。換句話說,你可能在想太難。
我不認爲我們應該嘗試「鏡像真實的物體」。我認爲更多的是尋找與系統(域)環境中建模的行爲非常相似的真實生活對象。在遊戲中爲水果切點的水果課程可能與水果課程中的水果課程有着截然不同的行爲和屬性,遊戲中的角色圍繞水果收集點;或模擬人們吃水果。將行爲分配給以現實生活對象命名的類可以更容易地假設代碼模塊的行爲並推測其交互。
+1很好的例子。另一個例子是Fruit是Edible接口/類,而一個消費者對象,比如Person類的一個對象,具有Eat(Edible e)方法。這將導致myPerson.Eat(myEdible)非常類似於OP的猜測。 – Spoike 2009-11-16 11:52:25
protected virtual void OnEaten(){Barf(); } – 2010-10-23 00:06:56