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

时间:2011-11-17 15:23:29

标签: c# dependency-injection inversion-of-control ioc-container

标准新手免责声明:我是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
    }
}

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

那么有没有更正确的方法来设置此代码以最好地使用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
    }
}

我不喜欢我在两种情况下传递完全相同的东西(目标文件夹除外)。在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
    }
}

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

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

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

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

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

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

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

2 个答案:

答案 0 :(得分:17)

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

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

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

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

然后在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
    }
}

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

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

答案 1 :(得分:2)

在您的示例中,您实际传递的是依赖项,但此外,类需要 data 才能运行。

在您的情况下,听起来方法RenameFiles()CopyFiles()对传递给它们的参数进行操作。鉴于他们的名字,我认为可以使用不同的参数调用单个ImageFileGenerator实例上的方法。如果这是真的可以说参数应该在方法调用本身而不是构造函数。

另一方面,如果一个实例RenameFiles()CopyFiles()每个只使用相同的参数调用一次,则参数将是构造函数的良好候选者。

我个人会尝试避免必需的依赖项的属性注入 - 在这种情况下,构造函数注入更合适。