使用依赖注入在应用程序中保存字典的位置
我有一个遗留代码,并且在重构它时遇到问题。
在我的应用程序开始时,我从 WCF
加载到 App
(这是 SL
应用程序)用户列表的属性。
然后每个控件(用于发送电子邮件、查看日历和分配任务)都使用此属性,
(App.Current as App).Users
现在,我正在尝试为使用此列表的控件之一创建单元测试,但我陷入了困境。
我应该使用 App
作为参数进行构造函数注入(我正在使用 Unity)吗?或者也许引入一些类来保存这个列表?
I have a legacy code, and I have a problem with reconstructor it.
At start of my application I load from WCF
to property on App
(this is SL
application) list of users.
Then every control (for sending emails, view calendar and assigning tasks) use this property as
(App.Current as App).Users
Now, I'm trying to create Unit Test for one of controls that use this lists, and I'm stuck.
Should I make a Constructor Injection(I'm using Unity) with App
as parameter? Or maybe introduce some class to hold this list?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
更新了 OP 的实现,因为伪代码不完整。
我建议为所有应用程序服务创建一个接口,
将 IApplicationService 注入到您的模块中。
您可以将此接口用于应用程序提供的所有服务(可能您需要更多)。模拟单元测试的接口
OP的实现
Updated with OP's implementation as the pseudocode was incomplete.
I propose create an interface for all your application services
Inject IApplicationService to your modules.
You can use this interface for all the services the application provides(probably you will need more). Mock the interface for the unit tests
OP's implemantation
我将创建一个包装类来公开用户列表。在生产代码中,此类只是 App.Current 属性的包装器,并且可以通过 Unity 将其注入到构造函数中。
在单元测试中,您可以轻松模拟 App 参数并在构建新的 SUT 时传递它。
像这样的东西:
I would create a wrapper class that will expose the list of users. In production code this class will just be a wrapper around your App.Current property and it can be injected in the constructor trough Unity.
In your Unit Tests you can easily mock the App parameter and pass it when constructing a new SUT.
Something like:
对于 Silverlight,有一个名为 应用程序扩展服务 的扩展模型。
出于基础设施目的,这可能是比向应用程序类添加属性并来回转换
App.Current
更好的替代方案。该模型的缺点是创建一个单例,您必须为单元测试进行初始化。它还会隐藏您的消费类中对
Users
的依赖。您的用户似乎只是数据。使该数据成为可以在应用程序中的任何位置访问和编辑的环境上下文会令您烦恼。您不知道谁对这些数据做了什么以及何时做。这就像会话状态。
因此,明确对数据的依赖将是能够跟踪该数据滥用的第一步。
如果您认为创建一个具有
Users
属性的“数据持有者对象”有意义,或者直接将该数据注入到您的消费者中,则由您决定。如果数据多于用户
,那么很容易将所有数据放入同一个中央数据存储对象中,即使您的特定消费者不需要它们。For Silverlight there is an extension model called Application Extension Services.
For infrastructure purposes that might be a better alternative than adding properties to your app class and casting
App.Current
back and forth.Downside of that model is the creation of a singleton you would have to initialize for your unit tests. It would also hide the dependency on
Users
in your consuming classes.Your users seem to be just data. Making that data an ambient context which can be accessed and edited everywhere in your application will bite you. You don't know who does what with that data and when he does it. This is like a session state.
So making the dependency on your data explicit would be a first step to be able to track abuse of that data.
If it makes sense to you to create a "data holder object" that has a property for
Users
or directly inject that data into your consumers is up to you. If there is more data than justUsers
it is tempting to put all of them into the same central data store object, even if your specific consumers don't need them.吉米的回答很好,但可以提供很多内容,并且修复了一些错误。代码/说明下方解释了差异:
创建公共接口:
IUserService
编写实现
的
UserService
IUserService通过构造函数将
IUserService
注入到类中在本例中,以您的 MainWindow 为例:
区别:
将您的用户服务与中央应用程序服务
更好的模块化。此外,我使用
IApplicationService
来获取更集中/全局的数据,例如 Api 密钥、超时、清理、数据库准备等。返回
IEnumerable
而不是 <代码>列表这只是保持事物干燥并且不对您的消费类强加硬实例化的黄金经验法则。重构更容易/更安全,并且您的代码更具可扩展性。
使用方法而不是属性
这是偏好,但我认为在服务层中尽可能使用方法是明智的,以便您可以引入过滤器和重载或继续使用依赖项注入 - 例如,您可以添加
GetUsers(string lastName)
、GetUsers(string lastName, string firstName)
并为您的消费类维护一个干净的接口。在不使用
as
关键字的情况下投射App.Current
这是一个很好的做法,因为使用
as
关键字意味着当转换失败时它将返回 null,而不是抛出异常。我更喜欢例外,因为 99% 的情况下,如果你的转换失败,你的下一个操作也会失败。 :)享受!
Jimmy's answer is great, but can be provide quite a bit, and some errors fixed. Differences are explained at the bottom below the code/instructions:
Create a public interface:
IUserService
Write a
UserService
that implementsIUserService
Inject
IUserService
into classes via their ConstructorIn this case your MainWindow as an example:
Differences:
Separate your User Services from a central Application Service
Better modularity. In addition I use an
IApplicationService
for more central/global data like Api Keys, Timeouts, cleanup, DB prepping, etc.Return
IEnumerable<T>
instead ofList<T>
This is just a golden rule of thumb for keeping things dry and not imposing hard instantiations on your consuming classes. Refactoring is easier/safer, and your code more extensible.
Use methods instead of properties
This is preference, but I think it smart in a service layer to use methods where possible so that you can introduce filters and overloads or continue to use dependency injection - for example, you could add
GetUsers(string lastName)
,GetUsers(string lastName, string firstName)
and maintain a clean interface for your consuming classes.Cast
App.Current
without theas
keywordThis is a good practice because using the
as
keyword means when the cast fails it will return null, rather than throw an exception. I prefer the exception because 99% of the time, if your cast fails, your next operations will too. :)Enjoy!