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

本文关键字:关系 依赖 策略 参数 | 更新日期: 2023-09-27 18:24:27

具有此接口:

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()的参数创建具体的解析器依赖项?这一定是setter注入?这好吗?或者我可以采取其他策略吗?

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

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

您可以通过构造函数注入依赖项。只要有可能,我都会选择这种方式。这意味着您不能使用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容器实例化,也没有问题。

我认为您应该使用泛型类型:

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>只有那一行代码,我们甚至可以争辩说,您甚至可能希望不使用这种抽象(然而,一如既往:您的里程可能会有所不同)。