我有一个返回类型的Enumerable的接口。
public interface IMapper
{
IEnumerable<IContract> Get(params object[] objects);
}
它基本上接受一个或多个类型也未知的参数,并返回实现IContract
的任何类型的Enumerable。随意为此提出替代方案。
现在看起来很简单并且有效。但是,这是一个总是返回一个对象(类型为IContract)的场景。我无法改变它只返回一个对象的事实。
var escalationMapper = _factory.GetEscalationMapper();
//we only get one object with a list of triggers but the interface returns a list. Change the interface?
var escalations = escalationMapper.Get(trackingGroupCode);
_factory.Release(escalationMapper);
var contracts = escalations as IList<IContract> ?? escalations.ToList();
response = Request.CreateResponse(!contracts.Any()
? HttpStatusCode.NotFound : HttpStatusCode.OK, contract);
我们感兴趣的代码行是注释之后的代码,因为我不愿意只返回一个对象并将其称为列表。 Get()方法还有其他用法可以返回正确的列表。这恰好最多返回1个对象。。
将单个对象作为列表返回是否合理,还是有更好的方法来实现它?
答案 0 :(得分:1)
这是一个权衡。目标是适应客户期望的类型。权衡在1)具有附加类和2)强制现有类型改变之间。
1)的pro是现有类型保持不变(假设它现在最有意义的类型,返回单个IContract); 1)的骗局是它需要更多的代码和更多的依赖。
2)的代表是代码大小和依赖数量仍然较低; 2)的骗局是你通过改变类型来破坏项目的类型设计,以返回一个总是包含单个元素的列表,纯粹是出于客户的期望。
在强类型语言中,类型系统旨在帮助程序员。更大的详细程度购买类型系统的好处,并且颠覆该系统对减少冗长度没有多大帮助,但却失去了 类型系统的好处。因此,我将使用适配器以强类型语言解决此问题,而不是通过更改接口。
换句话说,用最少惊喜(https://en.wikipedia.org/wiki/Principle_of_least_astonishment)原则解决问题。如果项目中的另一个程序员(或者你自己,在几个月后)通常会期望EscalationMapper的一个实例返回一个唯一的IContract,那么看到它返回一个IEnumerable会感到惊讶,然后使用一个适配器({{ 3}})。相反,如果看到它返回单个项目,他们会感到惊讶,然后更改界面。
答案 1 :(得分:1)
我同意@ Abion47和@chrylis评论 - 0或1项是一个完全有效的案例集合案例。
但是 - 如果您不同意并且仍然想要一种方法来传达没有IEnumerable
语义的单个元素,则可以使用一个方法来重载Get
,该方法接受单个对象并返回单个IContract。这将优先于params []重载,因为object
将比params[] object
更好地匹配单个输入。
这看起来像是:
public interface IMapper
{
IContract Get(object input);
IEnumerable<IContract> Get(params object[] input);
}
假设找不到null
值,您的调用代码将变为:
var escalationMapper = _factory.GetEscalationMapper();
//we only get one object with a list of triggers but the interface returns a list. Change the interface?
var escalation = escalationMapper.Get(trackingGroupCode);
_factory.Release(escalationMapper);
response = Request.CreateResponse(contract == null ? HttpStatusCode.NotFound : HttpStatusCode.OK, contract);
我必须提醒一下,建议这样做很奇怪,我认为它会在界面中引入不诚实行为。我也认为很有可能破坏任何现有的代码,这些代码也只用一个输入来调用接口。
至于替换object
,您可以随时添加一些标记界面,例如IContractInput
,并将其传递到Get
而不是object
:
public interface IContractInput { /* Intentionally empty */ }
然后,使用IContractInput
标记任何输入类:
public class SomeInput : IContractInput { /* implementation.. */ }
通过这种方式,可以强类型地输入方法,以取代IContractInput
而非通用object
类型。
答案 2 :(得分:1)
就像我在评论中所说的那样,你的代码在设计上没有任何错误。即使您调用Get
的特定位置保证在返回列表中最多返回一个项目,但这并不意味着它将在其他任何调用{{1}的地方得到保证。 }}
如果你真的想简化它以便它返回单个对象而不是一个长度的列表,不过,我也不建议改变界面以获得额外的方法。这将破坏实现IMapper.Get
的所有类并迫使您实现该新方法,即使在新方法不会添加任何有用的地方也是如此。如果其他人将您的代码用于自己的目的,这将是一个特别大的问题,因为它会迫使他们执行重写。
然而,要解决这个问题,你可以做的一件事就是将新方法声明为扩展方法:
IMapper
然后你可以这样称呼它:
public static class IMapperExtensions
{
public static IContract GetSingle(this IMapper mapper, params object[] objects)
{
return mapper.Get(objects).FirstOrDefault();
}
}
这将为您提供所需的功能,而不会破坏您界面的任何现有实现。