这是否会产生任何代码异味或违反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原则?)
答案 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方法。但那并没有那么好