在我的应用程序中,我正在处理
IMyInterface
实例列表。不是全部,但其中一些另外也实现了IAnotherInterface
。请注意,IAnotherInterface
并非源自 IMyInterface
。遵循单一职责原则,我有一个单独的类,通过 IMyInterfaces
方法处理 Process
实例。现在我正在为设计选择而苦苦挣扎
Process(IEnumerable<IMyInterface> items)
,我在这个方法中过滤IAnotherInterface
Process(IEnumerable<IAnotherInterface> items)
,这意味着过滤必须由“客户端”在处理方法之外完成。为了更清楚地说明,这些是我正在挣扎的两个代码选项:
// alternative 1:
List<MyInterface> items = GetItems(); // code not shown here
foreach(var item in items)
{
// do some other processing before, not shown here
// pass items to Process(IEnumerable<IMyInterface> items)
myProcessor.Process(items);
// do some other processing afterwards, not shown here
}
// or alternative 2:
List<MyInterface> items = GetItems(); // code not shown here
foreach (var item in items)
{
// do some other processing before, not shown here
// pass items to Process(IEnumerable<IAnotherInterface> items)
// -> need to filter first
var filteredItems = filterForIAnotherInterface(items);
myProcessor.Process(filteredItems);
// do some other processing afterwards, not shown here
}
选择其中之一有什么好的理由吗?我自己的想法是,替代方案 1 对客户端来说更容易使用,但是
Process
方法必须进行过滤,这除了其主要职责之外还增加了某种额外的职责。另一方面,我认为替代方案 2 在某种程度上使处理管道的可读性降低。
没有绝对的规则来指导这样的 API 设计决策。一方面,您可能希望尽可能明确。
显式优于隐式。
另一种表达方式是应用最小惊喜原则。如果您有一个带有签名
void Process(IEnumerable<IMyInterface> items)
的方法,客户端代码将期望这样的方法来处理 IMyInterface
对象。那么,这样的客户端可能会感到惊讶,如果它传递了一组IMyInterface
对象,而这些对象不也实现了IAnotherInterface
,然后什么也没有发生。令人惊讶。
另一方面,稳健性原则(波斯特尔定律)可能会认为,如果
Process
可以处理IMyInterface
对象,那么它也应该接受它们。
因为我不知道OP中的内容,所以听起来Postel定律真的并不适用于此,因为
Process
方法实际上不处理任何IMyInterface
对象 - 它只是忽略他们。
因此,在不了解更多信息的情况下,听起来 API 应该是
void Process(IEnumerable<IAnotherInterface> items)
。
另一方面,我认为替代方案 2 在某种程度上使处理管道的可读性较差。
只需使用OfType:
myProcessor.Process(items.OfType<IAnotherInterface>());
如果您确实想让管道显式化,您可以通过引入(内部)扩展方法来反转参数:
public static void ProcessWith(
this IEnumerable<IAnotherInterface> items,
SomeProcessor processor)
{
processor.Process(items);
}
这将使您能够像这样编写管道:
items.OfType<IAnotherInterface>().ProcessWith(myProcessor);