用户输入依赖注入的最佳策略是什么?
我已经使用了大量的依赖注入,但我想获得有关如何在运行时处理来自用户的信息的输入。
我有一个连接到 com 端口的类。我允许用户选择 com 端口号。现在,我将该 com 端口参数作为构造函数参数。原因是如果没有这些信息,该类就无法运行,并且它是特定于实现的(该类的模拟版本不需要 com 端口)。
另一种方法是使用一个接受 com 端口的“Start”方法,或者使用一个设置 com 端口的属性。这使得它与 IoC 容器非常兼容,但从类的角度来看它不一定有意义。
看起来逻辑路线与依赖注入设计相冲突,但这是因为我的 UI 正在获取特定类型的类的信息。
其他替代方案包括使用 IoC 容器,它允许我传递额外的构造函数参数,或者仅在顶层构造我需要的类,而不使用依赖注入。
对于此类问题是否有普遍接受的标准模式?
I've used a fair amount of dependency injection, but I'd like to get input on how to handle information from the user at runtime.
I have a class that connects to a com port. I allow the user to select the com port number. Right now, I have that com port parameter as a constructor argument. The reasoning being that the class cannot function without that information, and it's implementation specific (a mock version of this class wouldn't need a com port).
The alternative is to have a "Start" method that takes in the com port, or have a property that sets the com port. This makes it very compatible with an IoC container, but it doesn't necessarily make sense from the perspective of the class.
It seems like the logical route conflicts with the dependency injection design, but it's because my UI is getting information for a specific type of class.
Other alternatives would include using an IoC container that lets me pass in additional constructor parameters, or just constructing the classes I need at the top level without using dependency injection.
Is there a generally accepted standard pattern for this type of problem?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您可以选择两条路线,具体取决于您的需要。
1. 将 UI 直接连接到具体类
这是最简单的选项,但很多时候是完全可以接受的。虽然您可能有一个包含大量接口并使用 DI 的域模型,但 UI 构成了对象图的组合根,您可以简单地在此处连接您的具体类,包括所需的端口号参数。
优点是这种方法简单且易于理解和实施。
缺点是灵活性较差。您将无法任意将一种实现替换为另一种实现(但话又说回来,您可能不需要这种灵活性)。
即使 UI 锁定到具体实现,这并不意味着域模型本身不能在其他应用程序中重用。
2. 添加抽象工厂
另一种选择是添加另一层间接层。它可以使用抽象工厂来创建实例,而不是让 UI 直接创建类。
工厂的 Create 方法可以将端口号作为输入,因此这种抽象最适合 UI 子层。
然后,您可以让 DI 容器连接该工厂的实现,该工厂使用端口号并将其作为构造函数参数传递给您的实际实现。其他工厂实现可能会简单地忽略该参数。
这种方法的优点是您不会污染您的 API(或您的具体实现),并且您仍然拥有接口编程所提供的灵活性。
缺点是它增加了另一层间接性。
There are two routes you can take, depending on your needs.
1. Wire the UI directly to your concrete classes
This is the simplest option, but many times perfectly acceptable. While you may have a Domain Model with lots of interfaces and use of DI, the UI constitutes the Composition Root of the object graphs, and you could simply wire up your concrete class here, including your required port number parameter.
The upside is that this approach is simple and easy to understand and implement.
The downside is that you get less flexibility. You will not be able to arbitrarily replace one implementation with another (but then again, you may not need that flexibility).
Even with the UI locked to a concrete implementation, this doesn't mean that the Domain Model itself wouldn't be reusable in other applications.
2. Add an Abstract Factory
The other option is to add another layer of indirection. Instead of having your UI create the class directly, it could use an Abstract Factory to create the instance.
The factory's
Create
method could take the port number as an input, so this abstraction belongs best in a UI sub-layer.You could then have your DI container wire up an implementation of this factory that uses the port number and passes it as a constructor argument to your real implementation. Other factory implementations may simply ignore the parameter.
The advantage of this approach is that you don't pollute your API (or your concrete implementations), and you still have the flexibility that programming to interfaces give you.
The disadvantage is that it adds yet another layer of indirection.
大多数 IoC 容器都有某种形式的构造函数注入,允许您的 IoC容器将模拟的 COM 端口传递到您的类中以进行单元测试。这似乎是最干净的解决方案。
我会避免添加“Start”方法等。更好的做法是(如果可能)始终让您的类处于有效状态,并且使用 start 方法切换到无参数构造函数会使您的类在这些调用之间无效。这样做来启用测试只会让你的类更难测试(这应该会让它更好)。
Most IoC containers have some form of Constructor Injection that would allow your IoC container to pass a mocked COM port into your class for unit testing. That seems like the most clean solution.
I would avoid adding a "Start" method, etc. Its much better practice to (when possible) always have your classes in a valid state, and switching to a parameterless constructor with a start method leaves your class invalid between those calls. Doing this to enable testing is just making your class more difficult in order to test (which should make it nicer).