依赖于参数时使用不同依赖关系的依赖关系策略

时间:2014-09-25 06:07:42

标签: c# dependency-injection

有了这个界面:

public interface FileManager {
    string UploadFile(HttpPostedFileBase file);
    string UploadFile(Uri uri);
}

我的实现将如下所示:

public class FileManagerAzure : FileManager {
    private FileParser parser;
    FileManagerAzure(FileParser parser){
        this.parser = parser; // Can this be a constructor injection??
    }

    public string UploadFile(HttpPostedFileBase file) {
        return parser.Parse(file); // Should I Inject the parser here depending on type ??
    }
    public string UploadFile(Uri uri) {
        return parser.Parse(uri);
    }
}

这依赖于FileParser,如下所示:

public interface FileParser {
    string Parse(object source)
}

理想情况下我想要一些解析器实现(当然这不起作用):

public class FileParserHttpPostedFileBase : FileParser {
    string Parse(HttpPostedFileBase source) {
        return file.FileName;
    }
}

public class FileParserUri : FileParser {
    string Parse(Uri source) {
        return Uri.ToString();
    }
}
  1. 是否还要根据传递给UploadFile()的参数创建具体的解析器依赖项?这必须是一个二传手注射?这是好的,还是我可以遵循的任何其他策略?

  2. 我必须让我的FileParser接口接收一个对象作为源。这听起来不应该是因为我有一个有限的设置允许输入类型,当然不是object。在这种情况下HttpPostedFileBaseUri。我该如何限制此范围?

2 个答案:

答案 0 :(得分:3)

您可以通过构造函数注入依赖项。这就是我尽可能选择的方式。这意味着您无法使用IoC容器进行实例化。您可以使用抽象工厂模式来封装要注入的具体解析器的决策,并让IoC容器将具体工厂注入到调用类中。

但是,似乎有点矫枉过正。由于您的FileManager接口公开两种参数类型的方法,为什么不注入两种解析器类型?我为FileParser类型设置了通用类型,因此您不需要为每个新解析器提供新接口。

public interface FileParser<T>
{
    string Parse(T value);
}

public class UriParser : FileParser<Uri>
{
    string Parse(Uri value)
    {
        // implementation
    }
}

您可以像这样实现FileManagerAzure

public class FileManagerAzure : FileManager {
    private FileParser<Uri> uriParser;
    private FileParser<HttpPostedFileBase> postedFileParser;
    FileManagerAzure(FileParser<Uri> uriParser, FileParser<HttpPostedFileBase> postedFileParser){
        this.uriParser = uriParser;
        this.postedFileParser = postedFileParser;
    }

    public string UploadFile(HttpPostedFileBase file) {
        return this.postedFileParser.Parse(file);
    }
    public string UploadFile(Uri uri) {
        return this.uriParser.Parse(uri);
    }
}

这个实现可以通过IoC容器实例化,也没问题。

答案 1 :(得分:2)

我认为你应该使用泛型输入:

public interface FileParser<T> {
    string Parse(T source)
}

通过这种方式,您可以轻松实现以下多种实现:

public class FileParserHttpPostedFileBase : FileParser<HttpPostedFileBase> {
    public string Parse(HttpPostedFileBase source) {
        return file.FileName;
    }
}

public class FileParserUri : FileParser<Uri> {
    public string Parse(Uri source) {
        return Uri.ToString();
    }
}

这样你就可以消除设计中的模糊性,因为现在很清楚注入构造函数的内容:

public class FileManagerAzure : FileManager {
    private FileParser<HttpPostedFileBase> httpParser;
    private FileParser<Uri> uriParser;
    FileManagerAzure(FileParser<HttpPostedFileBase> httpParser, 
        FileParser<Uri> uriParser){
        this.httpParser = httpParser;
        this.uriParser = uriParser;
    }

    public string UploadFile(HttpPostedFileBase file) {
        return httpParser.Parse(file);
    }
    public string UploadFile(Uri uri) {
        return uriParser.Parse(uri);
    }
}

但是,如果FileManagerAzure仅委托其依赖关系,那么您应该质疑FileManager抽象是否有用。 FileManager的使用者可以直接依赖于FileParser<T>抽象之一。如果FileParser<T>只有一行代码,我们甚至可以争辩说你甚至可能想要没有这种抽象(但是,一如既往:你的里程可能会有所不同)。