DI的优点,据我所知有:
假设我有一个存储库 OrderRepository,它充当通过 Linq to Sql dbml 生成的订单对象的存储库。我无法使我的订单存储库通用,因为它执行 Linq Order 实体和我自己的 Order POCO 域类之间的映射。
由于 OrderRepository 必然依赖于特定的 Linq to Sql DataContext,因此不能真正说 DataContext 的参数传递可以使代码可重用或以任何有意义的方式减少依赖性。
这也使代码更难阅读,至于实例化我现在需要编写的存储库
new OrdersRepository(new MyLinqDataContext())
这也与存储库的主要目的相反,即从消费代码中抽象/隐藏 DataContext 的存在。
所以总的来说,我认为这将是一个非常糟糕的设计,但它会带来促进单元测试的好处。这是足够的理由吗?或者有第三种方法吗?我很想听听意见。
最佳答案
依赖注入(inject)的主要优势是测试。当我第一次开始采用测试驱动开发和 DI 时,您遇到了一些对我来说似乎很奇怪的事情。 DI 确实破坏了封装。单元测试应该测试与实现相关的决策;因此,您最终会暴露在纯封装场景中不会暴露的细节。您的示例很好,如果您不进行测试驱动开发,您可能希望封装数据上下文。
但是你说,因为 OrderRepository 必然依赖于特定的 Linq to Sql DataContext,我不同意 - 我们有相同的设置并且只依赖于一个接口(interface)。你必须打破这种依赖。
然而,让您的示例更进一步,您将如何在不使用数据库的情况下测试您的存储库(或它的客户端)?这是单元测试的核心原则之一 - 您必须能够不与外部系统交互来测试功能。没有什么比数据库更重要的了。依赖注入(inject)是一种可以打破对子系统和层的依赖的模式。没有它,单元测试最终需要大量的固定装置设置,变得难以编写、脆弱和太慢了。结果 - 你就是不会写它们。
将您的示例更进一步,您可能有
在单元测试中:
// From your example...
new OrdersRepository(new InMemoryDataContext());
// or...
IOrdersRepository repo = new InMemoryDataContext().OrdersRepository;
和生产中(使用 IOC 容器):
// usually...
Container.Create<IDataContext>().OrdersRepository
// but can be...
Container.Create<IOrdersRepository>();
(如果您还没有使用过 IOC 容器,那么它们就是使 DI 工作的粘合剂。将其视为对象图的“制作”(或 Ant )...容器为您构建依赖关系图,并且为施工做所有繁重的工作)。在使用 IOC 容器时,你会找回你在 OP 中提到的依赖隐藏。依赖项由容器作为一个单独的问题进行配置和处理 - 调用代码可以只请求接口(interface)的一个实例。
有一本非常棒的书详细探讨了这些问题。查看 xUnit 测试模式:重构测试代码,作者 Mezaros。这是将您的软件开发能力提升到一个新水平的书籍之一。
关于c# - 仅可测试性就可以证明依赖注入(inject)的合理性吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2985758/
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html
我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的
我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法
我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我喜欢使用Textile或Markdown为我的项目编写自述文件,但是当我生成RDoc时,自述文件被解释为RDoc并且看起来非常糟糕。有没有办法让RDoc通过RedCloth或BlueCloth而不是它自己的格式化程序运行文件?它可以配置为自动检测文件后缀的格式吗?(例如README.textile通过RedCloth运行,但README.mdown通过BlueCloth运行) 最佳答案 使用YARD直接代替RDoc将允许您包含Textile或Markdown文件,只要它们的文件后缀是合理的。我经常使用类似于以下Rake任务的东西: