我正试图接受广泛的依赖注入/ IoC。随着我越来越多地了解它的好处,我当然可以欣赏它们,但是我担心在某些情况下,接受依赖注入模式可能会导致我创建灵活性,代价是能够通过封装控制来限制风险系统能够做什么以及我或项目中的另一个程序员能够做出什么错误。我怀疑我错过了解决我的问题的模式中的某些内容,并希望有人可以指出它。
以下是关注我的简单示例。假设我在Notification类上有NotifyAdmins方法,并且我使用此方法将非常敏感的信息分发给已在应用程序中定义为管理员的用户。可以基于用户定义的设置通过传真,电子邮件,IM等分发信息。此方法需要检索管理员列表。从历史上看,我将封装在方法中构建管理员集合,调用AdminSet类,或者调用UserSet类,该类调用要求管理员的一组用户对象,或者甚至通过直接调用数据库。然后,我可以调用Notification.NotifyAdmins方法,而不必担心会意外地向非管理员发送敏感信息。
我认为依赖注入要求我将管理列表作为参数(以一种或另一种形式)。这确实有助于测试,但是,是什么阻止我在调用代码和传入一组NonAdmins时犯了一个愚蠢的错误?如果我没有注射该套装,我只能在一两个固定的地方不小心给错误的人发错。如果我注射该套件,我不会在任何地方犯这个错误我调用该方法并注入一组管理员?难道我做错了什么? IoC框架中是否有允许您指定这些约束但仍使用依赖注入的工具?
感谢。
答案 0 :(得分:4)
你需要改变你的想法。
如果您的服务/类应该仅将私人信息邮寄给管理员,而不是将管理员列表传递给此服务,而是传递另一个服务,该类可以从中检索管理员列表。< / p>
是的,你仍然有可能犯错,但是这段代码:
AdminProvider provider = new AdminProvider();
Notification notify = new Notification(provider);
notify.Execute();
比这更难弄错:
String[] admins = new String[] { "joenormal@hotmail.com" };
Notification notify = new Notification(admins);
notify.Execute();
在第一种情况下,所涉及的方法和类别的命名方式应该很容易发现错误。
在Execute方法的内部,代码可能如下所示:
List<String> admins = _AdminProvider.GetAdmins();
...
如果出于某种原因,代码如下所示:
List<String> admins = _AdminProvider.GetAllUserEmails();
然后你有问题,但这应该很容易发现。
答案 1 :(得分:0)
不,依赖注入不要求您将管理列表作为参数传递。我觉得你有点误会了。但是,在您的示例中,它将涉及您注入AdminSet
类用于构建其管理列表的Notification
实例。这样就可以模拟出这个对象来单独测试Notification
类。
依赖关系通常在实例化类时注入,使用以下方法之一:构造函数注入(在类的构造函数中传递依赖类实例),属性注入(将依赖类实例设置为属性)或其他(例如使所有可注入对象实现一个特定的接口,允许IOC容器调用一个注入其依赖关系的方法。通常不会像你建议的那样将它们注入到每个方法调用中。
答案 2 :(得分:0)
已经给出了其他好的答案,但我想补充一点:
您可以同时开放以实现可扩展性(Open/Closed Principle之后)并仍然保护敏感资产。一个好方法是使用Specification模式。
在这种情况下,您可以传入完全任意的用户列表,但然后通过AdminSpecification过滤这些用户,以便只有管理员才能收到通知。
也许你的Notification类会有类似这样的API:
public class Notification
{
private readonly string message;
public Notification(string message)
{
this.message = message;
this.AdminSpecification = new AdminSpecification();
}
public ISpecification AdminSpecification { get; set; }
public void SendTo(IEnumerable users)
{
foreach(var u in users.Where(this.AdminSpecification.IsSatisfiedBy))
{
this.Notify(u);
}
}
// more members
}
您仍然可以通过分配不同的规范来覆盖过滤行为以进行测试,但默认值是安全的,因此您不太可能在使用此API时出错。
为了获得更好的保护,您可以将整个实现包装在Facade接口之后。