有了这个界面:
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();
}
}
是否还要根据传递给UploadFile()
的参数创建具体的解析器依赖项?这必须是一个二传手注射?这是好的,还是我可以遵循的任何其他策略?
我必须让我的FileParser
接口接收一个对象作为源。这听起来不应该是因为我有一个有限的设置允许输入类型,当然不是object
。在这种情况下HttpPostedFileBase
和Uri
。我该如何限制此范围?
答案 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>
只有一行代码,我们甚至可以争辩说你甚至可能想要没有这种抽象(但是,一如既往:你的里程可能会有所不同)。