2015-02-11 18 views
0

考慮到DRY原則,您將如何處理幾乎相同的方法(使用不同的簽名),這些方法適用於IEnumerable。即一個簽名與特定的類型參數一起工作。我的問題擴展到私人方法的調用以及它們的多重簽名。使用多個方法簽名時的DRY原理

我不想有兩個方法具有相同的邏輯 - 如果有什麼改變,那麼我必須改變這兩套邏輯。的私有方法,例如主叫,我怎樣才能使私有方法接受任一類型的IEnumerable

public class Person 
{ 
    public string Name {get; set;} 
    public string Age {get; set;} 
} 

public class SupremeBeing : Person 
{ 
    public string Power {get; set;} 
} 

public class Payroll 
{ 
    public void DoSomething(IEnumerable<Person> peopleIn) 
    { 
     // Do this with peopleIn 
     // Do that with peopleIn 
     // Call private method passing in peopleIn (which also has 2 signatures) 
    } 

    public void DoSomething(IEnumerable<SupremeBeing> peopleIn) 
    { 
     // Do this with peopleIn 
     // Do that with peopleIn 
     // Call private method passing in peopleIn (which also has 2 signatures) 
    }  
} 
+2

問題是? – Jamiec 2015-02-11 12:19:03

+0

在這裏添加問題可以使問題更好。 – 2015-02-11 12:20:11

+0

也許你可以使用某種抽象,就像你最初的繼承方案一樣。這樣你就可以減少到一個採用最抽象IEnumerable的方法。 – 2015-02-11 12:20:31

回答

1

一種選擇是從第二個呼叫第一種方法:

public void DoSomething(IEnumerable<SupremeBeing> peopleIn) 
{ 
    this.DoSomething(peopleIn.Cast<Person>()); 

    // do SupremeBeing specific stuff 
} 

另一種選擇是有一個私人的方法,做所有的Person的東西。

public void DoSomething(IEnumerable<SupremeBeing> peopleIn) 
{ 
    this.DoSomethingWithPersons(peopleIn); 
} 

public void DoSomething(IEnumerable<Person> peopleIn) 
{ 
    this.DoSomethingWithPersons(peopleIn); 
} 

private void DoSomethingWithPersons(IEnumerable<Person> peopleIn) 
{ 
    // do stuff 
} 

兩個選項之間的細微差異沒有更多的信息,很難知道哪個更好。

+0

你不需要明確地說明,因爲SupremeBeing繼承人 – 2015-02-11 12:23:23

+0

你確實有爲:選擇最具體的過載,在這種情況下會導致反饋循環和「StackOverflowException」。 Casting確保選擇'IEnumerable '過載。 – 2015-02-11 12:24:21

3

它看起來對我來說,你想要的是在Payroll類更抽象

public abstract class PayrollBase<T> where T : Person 
{ 
    public void DoSomething(IEnumerable<T> peopleIn) 
    { 
     // Do this with peopleIn 
     // Do that with peopleIn 
     this.InternalLogic(peopleIn); 
    } 

    protected virtual InternalLogic(IEnumerable<T> peopleIn) 
    { 
     // do something super special 
    } 
} 

你可以這樣實現這爲您的特定類型的

public PersonPayroll : PayrollBase<Person> 
{ 
    protected override InternalLogic(IEnumerable<Person> peopleIn) 
    { ... } 
} 


public SupremeBeingPayroll : PayrollBase<SupremeBeing> 
{ 
    protected override InternalLogic(IEnumerable<SupremeBeing> peopleIn) 
    { ... } 
} 

你會然後使用某種形式廠類來實例化正在處理的人員列表中正確的「工資單」。

+0

我應該說,在我的現實生活中,Payroll類將是一個從視圖中調用的靜態html助手。但我明白你在說什麼 – ManxJason 2015-02-11 12:28:35

+0

我們也有一個叫做「模板方法模式」的模式名稱,它與「奇怪的循環模板模式」結合在一起 – 2015-02-11 12:37:24

0

我不明白你的問題。你可以創建一個通用的方法來完成你的通用內容,然後創建一個可以被你的特殊內容覆蓋的方法。

像:

class Something 
{ 
    protected virtual void DoSomethingSpecial<TYPE>(TYPE item) 
    { 
    } 

    public void DoSomethingy<TYPE>(IEnumerable<TYPE> items) 
    { 
     foreach(TYPE item in items) 
     { 
      // do whatever you have to do for every type 

      // do whatever you have to do in special 
      this.DoSomethingSpecial(item) 
     } 
    } 
} 

代碼沒有測試剛纔輸入。

然後爲每個特殊情況創建一個類。在這些類中,您只需覆蓋每種類型的DoSomethingSpecial,就完成了。

2

面向對象的方法是讓類自己處理差異。例如,您可以使用虛擬方法爲每個類實現一個實現。

如果您可以將每個對象視爲Person對象而不考慮實際類型,那麼您只需要一組方法來處理工資單,而且您不需要爲每個課程分別使用單獨的列表調用它,而是可以將它們放在同一個列表中並調用該方法。

例子:

public class Person { 

    public string Name {get; set;} 
    public string Age {get; set;} 

    virtual public int Salary { get { return 1000 + Age * 10; } } 

    override public string ToString() { 
    return Name + "(" + Age + ")"; 
    } 

} 

public class SupremeBeing : Person { 

    public string Power {get; set;} 

    override public int Salary { get { return 5000 + Age * 7; } } 

    override public string ToString() { 
    return Power + " " + Name; 
    } 

} 

public class Payroll { 

    public void DoSomething(IEnumerable<Person> peopleIn) { 
    foreach (Person p in peopleIn) { 
     Console.log("{0} earns {1}", p, p.Salary); 
    } 
    } 

} 
+0

表面上我同意這個答案,除了我的經驗告訴我,這導致違反SRP。通常'Person'只是一個DTO,因此不知道如何做計算工資等事情 - 這是別的責任。在一個簡單的例子中,它*看起來很好,但在現實世界中,薪水計算必須轉到xyz其他服務。它迅速變得凌亂。 – Jamiec 2015-02-11 12:45:35

+0

@Jamiec:如果計算薪水應該在其他地方完成,那麼Person對象應該只有屬性和方法來提供計算所需的信息。 – Guffa 2015-02-11 13:28:03

0

你可能只是有工資類理清不同類型的人羣已運行所有常見的操作和前進的人合適的擴展方法之後。

interface IPerson { 
    string Name { get; set; } 
    string Age { get; set; } 

} 
public class Person : IPerson { 
    public string Name { get; set; } 
    public string Age { get; set; } 
} 
public class SupremeBeing : Person 
{ 
    public string Power { get; set; } 
} 

public class Payroll 
{ 
    public void DoSomething(IEnumerable<IPerson> peopleIn) 
    { 
     //..everyone does this and that 
     IEnumerable<Person> NormalPeople = peopleIn.OfType<Person>(); 
     if (NormalPeople.Count() > 0) DoSomethingNormalSpecific(NormalPeople); 

     IEnumerable<SupremeBeing> SupremeBeings = peopleIn.OfType<SupremeBeing>(); 
     if (SupremeBeings.Count() > 0) DoSomethingSupremeSpecific(SupremeBeings); 

    } 

    public void DoSomethingNormalSpecific(IEnumerable<Person> normalPeopleIn) 
    { 
     // just normal people 
    } 

    public void DoSomethingSupremeSpecific(IEnumerable<SupremeBeing> supremeBeingsIn) 
    { 
     // just Supreme Beings 
    } 
}