2008-12-31 50 views
3

說你有這兩種方法:OO樣式參數VS類型參數

數1:

void AddPerson(Person person) 
{ 
    // Validate person 
    if(person.Name != null && IsValidDate(person.BirthDate) 
    DB.AddPersonToDatabase(person); 
} 

數2:

void AddPerson(string name, DateTime birthDate) 
{ 
    Person p = new Person(name, birthDate); 
    DB.AddPersonToDatabase(person); 
} 

這兩種方法是最好的?我知道第一個更正確的OO明智,但我覺得第二個更具可讀性,並且您不必確定該對象是有效的,因爲參數確保這一點。我只是不喜歡在我將它們作爲參數傳遞的任何地方驗證對象。還有其他方法嗎?編輯: Thx爲所有答案。爲了澄清,在構造函數和IsValid方法中進行驗證當然是一種好方法,但在我的代碼中,人員的有效狀態通常取決於上下文,並且可能因方法而異。這當然可能是設計不好的標誌。

該代碼只是一個例子來描述問題。

回答

10

第一個不應該驗證person.Name和person.BirthDate - 它們應該由Person構造函數自動驗證。換句話說,如果你通過一個人,你應該知道它是有效的。

另一方面,您必須檢查person不是空引用。

有時值得使用像第二個版本一樣的方便方法,以避免不得不經常顯式調用構造函數。通常應該調用Person構造函數,然後將工作委派給第一個表單。

+0

+1 - 同樣值得注意的是,在#2中,客戶端與Person類是分離的,這在某些情況下可能有所幫助 – 2008-12-31 14:42:48

2

第一個優點是可以在不破壞現有代碼的情況下更改Person定義,只需要重新編譯。你可能會認爲第二個更具可讀性,但第一個更易於維護,是你的選擇。

1

我更喜歡前者(傳遞一個對象),因爲它減少了API與對象的耦合。如果您更改了Person對象,例如添加一個新的屬性,如Nickname這是以前不需要的,那麼在第一種情況下,您不需要更改公共API,而在第二種情況下,您需要更改方法或添加新的重載。

0

我會大部分時間去第一個。

第二個會破壞簽名在人

0

的每一個變化這取決於上下文。

如果您的所有調用方法都處理Person對象,那麼第一個是正確的解決方案。

但是,如果您的某些調用方法涉及名稱和生日,並且有些涉及Person對象,那麼第二個示例就是正確的解決方案。

0

我只會爲兩者創建重載。特別是考慮到它們可以自動創建。

2

另一種選擇是:

void AddPerson(Person person) 
{ // Validate person 
    if(person.IsValid) 
    { 
    DB.AddPersonToDatabase(person); 
    } 
} 

假設人是在建造時不驗證本身。在某些情況下,如果對象在暫態時可能是無效狀態,哪種情況是可行的。

1

我同意它完全取決於上下文,對此沒有絕對的規則。在我看來,這將是無稽之談有這樣的方法:

person.SetBirthDate(Person person) 
person.ResetPassword(Person person) 

但在這種情況下,我做喜歡前者,因爲,作爲格雷格櫸木說,該方法不(要)知道什麼域對象。

順便說一句,考慮超載(DRY - 不要重複自己):

void AddPerson(Person person) 
{ 
    if(person.Name != null && IsValidDate(person.BirthDate) 
    DB.AddPersonToDatabase(person); 
} 

void AddPerson(string name, DateTime birthDate) 
{ 
    Person p = new Person(name, birthDate); 
    this.AddPerson(p); 
} 
0

我假設的方法不是Person類型的一部分。考慮到這一點,我覺得他們都對另一種類型(Person)有太多瞭解。第一個版本不應該驗證創建有效的人員實例。如果這是主叫方的責任,那麼每個主叫方都必須這樣做。這很脆弱。

第二個版本對另一個類型具有很強的依賴性,因爲它創建了這種類型的實例。

我肯定會更喜歡第一個版本,但我會從這段代碼移動驗證部分。

0

我覺得喬恩幾乎是釘了它。 Person應該負責確保創建一個有效的人爲其構造函數。

關於誰創建或不創建Person對象(addPerson的方法或者其是否打電話),讀

http://en.wikipedia.org/wiki/GRASP_%28Object_Oriented_Design%29#Creator

這是關於在面向對象的責任問題resonsibility。在你的具體情況下,如果AddPerson將調用封裝到數據庫接口,我不太確定。它取決於該對象用於該上下文之外的內容。如果僅用於包含要添加到數據庫中的數據,則在AddPerson方法中創建它可能是一個好主意,因爲它將您的類的用戶分離出來,不必知道Person類。 。

0

更好的封裝,如果你使用的對象;這就是他們的目標。我認爲人們在使用原語時會陷入困境。

應該不可能創建一個無效的Person。構造函數應該檢查有效的參數並拋出IllegalArgumentException,或者如果它們無效,則會失敗。這就是合約編程的全部內容。

1

肯定會傳遞一個Person對象,而不是一堆原始類型作爲參數。比較以下兩種方法


public static void Withdrawal(Account account, decimal amount) 
{ 
    DB.UpdateBalance(account.AccountNumber, amount); 
} 

public static void Withdraw(int accountNumber, decimal amount) 
{ 
    DB.UpdateBalance(accountNumber, amount); 
} 

這兩種方法看起來幾乎相同,但第二種是不安全的。int可以來自任何地方,這樣你就完蛋了,如果你這樣寫:

 
private void CloseTransaction(Transaction tran) 
{ 
    BankAccounts.Withdrawal(tran.Account.RoutingNumber, tran.Amount); 
     // logic error: meant to pass Account.AccountNumber instead of Account.RoutingNumber 
} 

這是最壞的一種錯誤的,因爲它不會拋出一個編譯錯誤或運行時異常。如果你寫得夠好,你可能會在自動化測試中發現這個錯誤,但是這個錯誤很容易漏掉,並且可能會隱藏好幾個月而不會被發現。

我曾經在一家寫銀行軟件的公司工作過,而且我們確實在生產中遇到過這種類型的錯誤。它只發生在某種特定類型的金庫轉移過程中,只有當我們的一家銀行每次運行月末處理時都注意到它們的總帳餘額減少了幾百美元時才發現。該銀行懷疑員工被盜數月,但只有通過仔細的代碼審查,才能將問題追溯到我們軟件中的一個錯誤。