Are primitive constructor parameters a bad idea when using an IoC Container?
标准的新手免责声明:我是国际奥委会的新手,收到的信号不一。我在找以下情况的指导。
假设我有以下接口和实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public interface IImageFileGenerator { void RenameFiles(); void CopyFiles(); } public class ImageFileGenerator : IImageFileGenerator { private readonly IList<IImageLink> _links; private readonly string _sourceFolder; private readonly string _destinationFolder; private readonly int _folderPrefixLength; public ImageFileGenerator(IList<IImageLink> links, string sourceFolder, string destinationFolder) { _links = links; _sourceFolder = sourceFolder; _destinationFolder = destinationFolder; _folderPrefixLength = 4; } public void RenameFiles() { // Do stuff, uses all the class fields except destination folder } public void CopyFiles() { // Do stuff, also uses the class fields } } |
我很困惑,我是应该只向构造函数发送接口/依赖项,创建一些参数对象并将其传递给构造函数,还是保持原样,并在解析实例时传递参数。
那么,有没有一种更正确的方法来设置这个代码,以便与IOC容器一起工作?以下哪一项在设计上比我目前的布局更合适?
1。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public interface IImageFileGenerator { void RenameFiles(IList<IImageLink> links, string sourceFolder); void CopyFiles(IList<IImageLink> links, string sourceFolder, stringDestFolder); } public class ImageFileGenerator : IImageFileGenerator { private readonly int _folderPrefixLength; public ImageFileGenerator() { _folderPrefixLength = 4; } public void RenameFiles(IList<IImageLink> links, string sourceFolder) { // Do stuff } public void CopyFiles(IList<IImageLink> links, string sourceFolder, stringDestFolder) { // Do stuff } } |
我不喜欢在这两种情况下传递完全相同的内容(除了目标文件夹)。在IImageFileGenerator的当前实现中,我需要执行这两个方法,并且每个方法都需要相同的值。这就是我通过构造函数传入状态的原因。
2。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | public interface IImageFileGenerator { void RenameFiles(); void CopyFiles(); } public class ImageLinkContext { // various properties to hold the values needed in the // ImageFileGenerator implementation. } public class ImageFileGenerator : IImageFileGenerator { private readonly IList<IImageLink> _links; private readonly string _sourceFolder; private readonly string _destinationFolder; private readonly int _folderPrefixLength; public ImageFileGenerator(ImageLinkContext imageLinkContext) { // could also use these values directly in the methods // by adding a single ImageLinkContext field and skip // creating the other fields _links = imageLinkContext.ImageLinks; _sourceFolder = imageLinkContext.Source; _destinationFolder = imageLinkContext.Destination; _folderPrefixLength = 4; } public void RenameFiles() { // Do stuff, uses all the class fields except destination folder } public void CopyFiles() { // Do stuff, uses all the class fields } } |
这种方法甚至可以调整为一个门面服务(以前称为聚合服务),如这里马克·西曼所提到的。
我也读过,您可以使用这些值的属性,并使用属性注入,尽管这似乎不再是首选(autopac提到构造函数注入是首选的…我相信在第2版中甚至删除了这个功能)。
或者,我已经阅读到您还可以创建一个初始化方法,并确保在其中设置了属性。
有这么多的选择,我越来越困惑,因为我读了更多关于这个东西。我确信没有明确的正确方法(或者可能有,至少在这个例子中是这样的)???)但是也许有人可以提供每种方法的优缺点。或许还有另一种方法我完全错过了。
我现在意识到这个问题可能有点主观(实际上不止一个问题),但我希望你能原谅我并提供一些指导。
PS-我目前正在尝试自动空调,以防影响哪种设计可能更适合。
注意:我对目标文件夹的代码做了一些细微的更改…Renamefiles没有使用它(可能与您的答案有关)。
好吧,我在阅读了.NET中的书籍依赖项注入(我强烈推荐这本书给任何面向对象的开发人员,不仅仅是.NET开发人员,也不仅仅是那些有兴趣使用IOC容器的开发人员!).
我现在在域程序集中得到了以下内容:
1 2 3 4 5 6 7 8 9 10 | public interface IImageFileService { void RenameFiles(); void CopyFiles(); } public interface IImageLinkMapRepository { IList<ImageLink> GetImageLinks(); } |
然后在FileAccess程序集中,我为这些接口创建了实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class ImageFileService : IImageFileService { public ImageFileService(IImageLinkMapRepository repository) { // null checks etc. left out for brevity _repository = repository; } public void RenameFiles() { // rename files, using _repository.GetImageLinks(), which encapsulates // enough information for it to do the rename operations without this // class needing to know the specific details of the source/dest dirs. } public void CopyFiles() { // same deal as above } } |
所以本质上,我已经消除了对构造函数中基本类型的需求,至少对于这个类是这样的。在某种程度上,我确实需要这些信息,但是这些信息被注入到ImageLinkMapRepository中,在那里信息更加合理。我用autopac命名参数来处理注入。
所以我想回答我自己的问题,如果原始构造函数参数有意义的话,它们是一个好主意,但是要确保将它们放在它们所属的位置。如果事情看起来不太顺利,重新考虑设计可能会有所改善。
在您的示例中,您实际传递的是依赖项,而且是类操作所需的数据。
在您的例子中,听起来像是方法
另一方面,如果在一个实例中,
我个人会尽量避免对所需依赖项进行属性注入——在这种情况下,构造函数注入更为合适。