使用 IoC 容器时,原始构造函数参数是一个坏主意吗? [英] Are primitive constructor parameters a bad idea when using an IoC Container?

查看:22
本文介绍了使用 IoC 容器时,原始构造函数参数是一个坏主意吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

标准新手免责声明:我是 IoC 的新手并且收到了混合信号.我正在寻找有关以下情况的一些指导.

Standard newbie disclaimer: I'm new to IoC and am getting mixed signals. I'm looking for some guidance on the following situation please.

假设我有以下接口和实现:

Suppose I have the following interface and implementation:

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
    }
}

我很困惑是应该只将接口/依赖项发送给构造函数,创建一些参数对象并将其传递给构造函数,还是保持原样并在解析实例时传入参数.

I'm getting confused whether I should only send interface/dependencies to the constructor, create some parameter object and pass it to the constructor or keep it as is and pass in the parameters at the time of resolving an instance.

那么有没有更正确的方法来设置这段代码以最好地与 IoC 容器配合使用?与我当前的布局相比,以下任何一项在设计上都更受欢迎吗?

So is there a more correct way of setting up this code to work best with an IoC container? Would either of the following be preferred design-wise over my current layout?

1.

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 的当前实现中,我需要执行这两个方法,并且每个方法都需要相同的值.这就是我通过构造函数传入状态的原因.

I don't like that I'm passing in the exact same thing in both cases (except the destination folder). In the current implementation of the IImageFileGenerator, I need to execute both methods and the same values were needed for each method. That is why I passed the state in via the constructor.

2.

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
    }
}

这种方法甚至可能被调整为 Facade Service(以前称为聚合服务),正如 Mark Seemann 此处.

This approach may even be tweaked to a Facade Service (previously called aggregate services) as mentioned by Mark Seemann here.

我还读到您可以为这些值使用属性并使用属性注入,尽管这似乎不再是首选(autofac 提到构造函数注入是首选... Ninject 我相信甚至删除了版本 2 中的功能).

I've also read that you could use properties for those values and use property injection, though it seems like that is not preferred anymore (autofac mentions constructor injection is preferred... Ninject I believe even removed the ability in version 2).

或者,我读到您还可以创建一个 initialize 方法并确保在其中设置属性.

Alternatively I've read that you can also create an initialize method and ensure that the properties are set in there.

这么多的选择,当我阅读更多关于这些东西的信息时,我越来越困惑.我敢肯定没有确定的正确方法(或者也许有,至少对于这个例子???),但也许有人可以提供每种方法的优缺点.或者也许还有另一种我完全错过的方法.

So many options and I'm getting more confused as I read more about this stuff. I'm sure there is no definitive correct way (or maybe there is, at least for this example???), but maybe someone can provide pros and cons of each approach. Or maybe there is another approach that I've totally missed.

我现在意识到这个问题可能有点主观(实际上不止一个问题),但我希望你能原谅我并提供一些指导.

I realize now that this question is probably a little on the subjective side (and is really more than one question), but I'm hoping you can forgive me and provide some guidance.

PS - 我目前正在尝试使用 autofac,以防影响哪种设计更适合.

PS - I'm currently trying my hand with autofac in case that influences which design may fit better.

注意:我对有关目标文件夹的代码做了一些细微的更改... RenameFiles 不使用它(可能会影响您的答案).

推荐答案

好吧,我在阅读了 Dependency Injection in .Net(我向所有面向对象的开发人员强烈推荐这本书,而不仅仅是 .Net 开发人员,也不仅仅是那些对使用 IoC 容器感兴趣的人!)

Well I ended up redesigning this after reading the book Dependency Injection in .Net (I highly recommend this book to any object-oriented developer, not just .Net developers and not just those interested in using an IoC container!).

我现在在域程序集中有以下内容:

I've now got the following in a Domain assembly:

public interface IImageFileService
{
    void RenameFiles();
    void CopyFiles(); 
}

public interface IImageLinkMapRepository
{
    IList<ImageLink> GetImageLinks(); 
}

然后在 FileAccess 程序集中,我为这些接口创建了实现:

Then in a FileAccess assembly I've created implementations for these interfaces:

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 中,在那里信息更有意义.我使用 autofac 命名参数来处理注入它们.

So essentially, I've removed the need for primitive types in my constructor, at least for this class. At some point I did need that information, but that was injected into the ImageLinkMapRepository where the information made more sense. I used autofac named parameters to handle injecting them.

所以我想回答我自己的问题,原始构造函数参数是一个好主意,如果它们有意义,但请确保将它们放在它们所属的地方.如果事情看起来不正常,可能可以通过重新考虑设计来改进.

So I guess to answer my own question, primitive constructor parameters are a good idea if they make sense, but make sure you put them where they belong. If things don't seem to be jiving properly, it can probably be improved by rethinking the design.

这篇关于使用 IoC 容器时,原始构造函数参数是一个坏主意吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆