将方法绑定到实现类

时间:2010-09-03 15:45:55

标签: c# oop design-patterns

这是否会产生任何代码异味或违反SOLID原则?

public string Summarize()
{
IList<IDisplayable> displayableItems = getAllDisplayableItems();
StringBuilder summary = new StringBuilder();

foreach(IDisplayable item in displayableItems)
{
    if(item is Human)
        summary.Append("The person is " + item.GetInfo());

    else if(item is Animal) 
        summary.Append("The animal is " + item.GetInfo());

    else if(item is Building) 
        summary.Append("The building is " + item.GetInfo());

    else if(item is Machine) 
        summary.Append("The machine is " + item.GetInfo());
}

return summary.ToString();
}

正如您所看到的,我的Summarize()与实现类相关联,例如Human,Animal等。

此代码是否违反了LSP? (任何其他SOLID原则?)

7 个答案:

答案 0 :(得分:21)

我闻到了一些东西......

如果你的类都实现了IDisplayable,那么每个类都应该实现自己的逻辑来显示自己。这样你的循环就会变得更清洁:

public interface IDisplayable
{
    void Display();
    string GetInfo();
}

public class Human : IDisplayable
{
    public void Display() { return String.Format("The person is {0}", 
        GetInfo());

    // Rest of Implementation
}

public class Animal : IDisplayable
{
    public void Display() { return String.Format("The animal is {0}", 
        GetInfo());

    // Rest of Implementation
}

public class Building : IDisplayable
{
    public void Display() { return String.Format("The building is {0}", 
        GetInfo());

    // Rest of Implementation
}

public class Machine : IDisplayable
{
    public void Display() { return String.Format("The machine is {0}", 
        GetInfo());

    // Rest of Implementation
}

然后你可以将循环更改为更清晰的东西(并允许类实现自己的显示逻辑):

foreach(IDisplayable item in displayableItems)
    summary.Append(item.Display());

答案 1 :(得分:5)

似乎IDisplayable应该有一个显示名称的方法,所以你可以将该方法缩小为类似

summary.Append("The " + item.displayName() + " is " + item.getInfo());

答案 2 :(得分:3)

为什么不让每个类实现一个显示其类型的IDisplayable方法:

interface IDisplayable
{
    void GetInfo();
    public string Info;
}
class Human : IDisplayable
{
   public string Info
   { 
    get 
    { 
        return "";//your info here
    }
    set;
   }

   public void GetInfo()
   {
       Console.WriteLine("The person is " + Info)
   }
}

然后按如下方式调用您的方法:

foreach(IDisplayable item in displayableItems)
{
    Console.WriteLine(item.GetInfo());
}

答案 3 :(得分:1)

怎么样:

    summary.Append("The " + item.getType() + " is " + item.GetInfo()); 

答案 4 :(得分:1)

至少违反了LSP和开放原则。

解决方案是为Description接口提供IDisplayable属性,以便汇总可以调用

summary.Append(string.Format("The {0} is {1}", item.Description, item.GetInfo()));

这也可以通过反思解决,因为你只得到了这个类的名字。

更好的解决方案是从IDisplayableInfo方法返回类似GetInfo()的内容。这将是一个有助于保护OCP的可扩展性点。

答案 5 :(得分:1)

鉴于来自OP的this answer的评论,我认为最好的方法是创建一个自定义容器类来替换IList<IDisplayable> displayableItems,其中包含containsHumans()和{{1}等方法因此,您可以将icky非多态代码封装在一个位置,并保持containsAnimals()函数中的逻辑清洁。

Summarize()

当然,我的例子过于简单和做作。例如,要么作为class MyCollection : List<IDisplayable> { public bool containsHumans() { foreach (IDisplayable item in this) { if (item is Human) return true; } return false; } // likewise for containsAnimals(), etc } public string Summarize() { MyCollection displayableItems = getAllDisplayableItems(); StringBuilder summary = new StringBuilder(); if (displayableItems.containsHumans() && !displayableItems.containsAnimals()) { // do human-only logic here } else if (!displayableItems.containsHumans() && displayableItems.containsAnimals()) { // do animal-only logic here } else { // do logic for both here } return summary.ToString(); } if / else语句中逻辑的一部分,要么围绕整个块,您需要迭代Summarize()集合。此外,如果您覆盖displayableItems中的Add()Remove()并让他们检查对象的类型并设置标记,那么您可能会获得更好的效果,因此MyCollection函数(和其他函数)可以简单地返回标志的状态,而不必在每次调用时迭代集合。

答案 6 :(得分:0)

如果您无法修改IDisplayable或类实现,并且您使用的是.NET 3.5或更高版本,则可以使用Extension方法。但那并没有那么好