【发布时间】:2016-09-23 16:50:40
【问题描述】:
我是数以千计的开发人员使用的几个 C# 库的作者。我经常被要求自定义实现以启用边缘情况。我使用了以下方法,每种方法都有其优点。请允许我列出它们,如果没有其他原因,那么对可扩展性感兴趣的新手开发人员可能会开始看到可供他们使用的模式。
继承。我在开发人员可以继承的非密封类中使用abstract 和virtual 方法,并根据他们的逻辑使用override。
public class DefaultLibrary
{
public virtual void MyMethod()
{
// default logic
}
}
public class CustomLibrary : DefaultLibrary
{
public override void MyMethod()
{
// custom logic
}
}
Caveat> 有时你写的类必须是sealed。事实上,sealed 是您编写库时类的一个很好的默认值。在这种情况下,您需要考虑其他类似...
构造函数注入。我在一个类中使用了optional 构造参数,使开发人员能够传入自定义逻辑。
public interface IService
{
void Process();
}
public class DefaultLibrary
{
IService _service;
public DefaultLibrary(IService service)
{
_service = service;
}
public virtual void MyMethod()
{
_service.Process();
}
}
Caveat> 有时,您正在编写的类需要维护一个内部状态,该状态要求它们是单例(维护一个 static 实例)。在这种情况下,您需要考虑其他类似...
属性注入。我使用了类似工厂的属性,开发人员可以用他们自己的逻辑覆盖类的默认实现。
public interface IService
{
void Process();
}
public class DefaultLibrary
{
public IService Service { get; set; }
public virtual void MyMethod()
{
Service.Process();
}
}
Caveat> 属性注入很好,但它的行为很像构造函数注入,因为它需要interface 和在具体class 中的interface 实现。有时您只是想让开发人员覆盖一个小的实现(一个或两个方法),就像继承(上图)但不需要基础。
这是我要解决的问题。
我想要一种让开发人员感觉更轻量且不会引入大量新移动部件的方法。所以,我已经登陆了。我想提出这种方法。我从未使用过它,也无法捍卫它的优点或缺陷。出于这个原因,我在问这个问题。 这种模式是合理的、明智的、有问题的还是一个绝妙的想法?看起来不错。
这种模式可能已经有了名字。我不知道。这是要点:
public class CustomLibrary
{
private void CallMyMethod()
{
MyMethod?.Invoke();
}
public Action MyMethod { get; set; }
}
这是一个完整的示例实现:
private async void CallSaveAsync(string value)
{
if (RaiseBeforeSave(value))
{
await SaveAsync?.Invoke();
RaiseAfterSave(value);
}
}
private Func<Task> _saveAsync;
public Func<Task> SaveAsync
{
get { return _saveAsync ?? DefaultSaveAsync; }
set { _saveAsync = value; }
}
private async Task DefaultSaveAsync()
{
await Task.CompletedTask;
}
短吗?方法是开发人员可以覆盖的属性。
从 API 表面来看,确实没有任何变化。开发人员仍然调用await class.SaveAsync() 并且它像宣传的那样工作。但是,开发人员现在可以选择使用class.SaveAsync = MyNewMethod,而不会中断使用前后事件包装方法的内部逻辑。
我马上看到的可接受的缺点:
- 我不能使用
ref参数 - 我不能使用
optional参数 - 我不能使用
params参数 - 我不能使用方法覆盖
除此之外,我看不出这种方法有什么严重的问题。当时间适合需要ref 或optional 的方法时,我将不得不更改模式。但是为什么不用完全像这样的所有候选方法来编写我的库呢?当然,这对我来说是更多的代码。但我不介意。
感谢您抽出宝贵时间。
【问题讨论】:
-
它似乎是Strategy 模式。
-
这过于基于意见。
-
@DanielA.White 确实如此。这个问题属于programmers.stackexchange.com - 即使目标群体可能正在这里阅读它。
-
@jaco0646:是的,但是策略模式具有强制性接口。如果我正确理解了这个问题,那么接口就是 OP 试图避免的。
-
@JerryNixon:在我看来,这实际上是一个稍微复杂一点的高阶函数。是什么阻止您简单地公开
Action<T>参数、Func<T>参数,甚至是Task<T>参数来完成同样的事情?
标签: c# design-patterns dependency-injection uwp