我试图向我的团队解释为什么这是不好的做法,我正在寻找一个反模式参考来帮助解释.这是一个非常大的企业应用程序,所以这里有一个简单的例子来说明实现的内容:
public void ControlStuff()
{
var listOfThings = LoadThings();
var listOfThingsThatSupportX = new string[] {"ThingA","ThingB", "ThingC"};
foreach (var thing in listOfThings)
{
if(listOfThingsThatSupportX.Contains(thing.Name))
{
DoSomething();
}
}
}
Run Code Online (Sandbox Code Playgroud)
我建议我们在'Things'基类中添加一个属性来告诉我们它是否支持X,因为Thing子类需要实现有问题的功能.像这样的东西:
public void ControlStuff()
{
var listOfThings = LoadThings();
foreach (var thing in listOfThings)
{
if (thing.SupportsX)
{
DoSomething();
}
}
}
class ThingBase
{
public virtual bool SupportsX { get { return false; } }
}
class ThingA : ThingBase
{
public override bool SupportsX { get { return true; } }
}
class ThingB : ThingBase
{
}
Run Code Online (Sandbox Code Playgroud)
所以,很明显为什么第一种方法是不好的做法,但这又叫什么呢?另外,有没有比我建议的更适合这个问题的模式?
And*_*rsK 76
通常,更好的方法(IMHO)将使用接口而不是继承
那么只需检查对象是否已实现接口.
Mis*_*ble 40
我认为反模式名称是硬编码的 :)
是否应该ThingBase.supportsX至少在某种程度上取决于什么X.在极少数情况下,知识可能只是在ControlStuff().
但更常见的情况X可能是,在某些情况下ThingBase可能需要使用ThingBase.supports(ThingBaseProperty)或某些方式公开其功能.
Dyl*_*ith 22
IMO这里的基本设计原则是封装.在您提出的解决方案中,您已将逻辑封装在Thing类中,而在原始代码中,逻辑会泄漏到调用者中.
它也违反了Open-Closed原则,因为如果你想添加支持X的新子类,你现在需要去修改包含那个硬编码列表的任何地方.使用您的解决方案,您只需添加新类,覆盖方法即可完成.
Sha*_*ard 11
不知道一个名字(怀疑这样存在),但想到每一个"东西"作为一辆车 - 有些车有巡航控制系统而其他人没有.
现在,您拥有自己管理的车队,并希望了解哪些车辆具有巡航控制权.
使用第一种方法就像查找所有具有巡航控制功能的汽车模型的列表,然后驾车开车并在该列表中搜索每一个 - 如果有的话意味着汽车有巡航控制,否则它没有.累赘吧?
使用第二种方法意味着每辆拥有巡航控制系统的汽车都带有贴纸,上面写着"我有巡航控制",您只需要查找贴纸,而无需依赖外部信息来为您提供信息.
不是非常技术性的解释,但简单而重要.
这种编码实践很有意义,这是一种非常合理的情况.它可能不是哪个东西实际上支持X的问题(当然每个东西上的接口会更好),而是支持X的东西是你想要启用的东西.您所看到的标签就是配置,目前是硬编码的,对此的改进是最终将其移动到配置文件或其他方式.在你说服你的团队改变它之前,我会检查这不是你所转述的代码的意图.
写太多代码反模式.这使得阅读和理解变得更加困难.
正如已经指出的那样,使用接口会更好.
基本上,程序员没有利用面向对象的原则,而是使用过程代码来做事.每当我们达到"if"语句时,我们应该问自己,我们是否应该使用OO概念而不是编写更多的过程代码.