使用IoC容器时原始构造函数参数是个坏主意吗?

Jas*_*own 36 c# dependency-injection ioc-container inversion-of-control

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

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

那么有没有更正确的方法来设置此代码以最好地与IoC容器一起使用?在我目前的布局中,以下任何一种都是首选设计吗?

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
    }
}
Run Code Online (Sandbox Code Playgroud)

我不喜欢我在两种情况下传递完全相同的东西(目标文件夹除外).在IImageFileGenerator的当前实现中,我需要执行两种方法,并且每种方法都需要相同的值.这就是我通过构造函数传递状态的原因.

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
    }
}
Run Code Online (Sandbox Code Playgroud)

这种方法甚至可以调整为Mark Seemann 在此提到的Facade Service(以前称为聚合服务).

我还读过你可以使用这些值的属性并使用属性注入,虽然它似乎不再是首选(autofac提及构造函数注入是首选... Ninject我相信甚至删除了版本2中的能力).

或者我已经读过你也可以创建一个初始化方法并确保在那里设置属性.

当我阅读更多有关这些内容的内容时,有太多的选择让我更加困惑.我敢肯定没有明确的正确方法(或者至少对于这个例子来说可能有),但也许有人可以提供每种方法的优缺点.或许还有另一种我完全错过的方法.

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

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

注意:我对目标文件夹的代码稍作修改...... RenameFiles没有使用它(可能与你的答案有关).

Jas*_*own 17

好吧,我在阅读了.Net中的Dependency Injection一书之后最终重新设计了这个(我强烈推荐这本书给任何面向对象的开发人员,而不仅仅是.Net开发人员,而不仅仅是那些对使用IoC容器感兴趣的人!).

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

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

public interface IImageLinkMapRepository
{
    IList<ImageLink> GetImageLinks(); 
}
Run Code Online (Sandbox Code Playgroud)

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)

基本上,我已经在构造函数中删除了对原始类型的需求,至少对于这个类来说.在某些时候,我确实需要这些信息,但是这被注入ImageLinkMapRepository,信息更有意义.我使用autofac命名参数来处理它们.

所以我想回答我自己的问题,原始的构造函数参数是一个好主意,如果它们有意义,但要确保你把它们放在它们所属的位置.如果事情看起来不合适,可以通过重新思考设计来改善.