System.IO.Abstractions正在使用Adapter Pattern。這是一種用於某些沒有任何抽象(抽象類或接口)的類型的技巧,以便在DI中使用它們。由於沒有辦法在.NET中爲現有類型添加抽象,因此將創建一個封裝(適配器),其中包含一個抽象(在本例中爲抽象類),以便用於鬆散地耦合實現。
這裏的問題是你沒有使用包裝,你直接使用實現。
IKernel ninject = new StandardKernel();
ninject.Bind<DirectoryInfoBase>().To<DirectoryInfoWrapper>()
.WithConstructorArgument("instance", new DirectoryInfo(@"C:\Somewhere\"));
但是,這裏還有一個問題 - DirectoryInfo需要一個目錄路徑作爲構造函數參數。所以這意味着使用Abstract Factory通常會更有意義,因此可以在目錄路徑已知時在運行時創建它。在這種情況下,將工廠注入到服務中然後調用該方法在運行時創建實例會更有意義。 System.IO.Abstractions的作者使工廠內部,但你可以建立一個一樣的。
[Serializable]
public class DirectoryInfoFactory : IDirectoryInfoFactory
{
public DirectoryInfoBase FromDirectoryName(string directoryName)
{
var realDirectoryInfo = new DirectoryInfo(directoryName);
return new DirectoryInfoWrapper(realDirectoryInfo);
}
}
public class SomeService : ISomeService
{
private readonly IDirectoryInfoFactory directoryInfoFactory;
public SomeService(IDirectoryInfoFactory directoryInfoFactory)
{
if (directoryInfoFactory == null)
throw new ArgumentNullException("directoryInfoFactory");
this.directoryInfoFactory = directoryInfoFactory;
}
public void DoSomething()
{
// The directory can be determined at runtime.
// It could, for example, be provided by another service.
string directory = @"C:\SomeWhere\";
// Create an instance of the DirectoryInfoWrapper concrete type.
DirectoryInfoBase directoryInfo = this.directoryInfoFactory.FromDirectoryName(directory);
// Do something with the directory (it has the exact same interface as
// System.IO.DirectoryInfo).
var files = directoryInfo.GetFiles();
}
}
然後配置容器以注入一個工廠,該工廠可以創建多個運行時實例而不是單個實例。
IKernel ninject = new StandardKernel();
ninject.Bind<IDirectoryInfoFactory>().To<DirectoryInfoFactory>();
但System.IO.Abstractions的作者還有另一個竅門讓它更進一步。他做了一個Aggregate Service,可以注入並提供許多類型在System.IO名稱空間中的服務以鬆散耦合的方式提供。
因此,您可以改爲使用現有的IFileSystem服務來訪問System.IO命名空間提供的幾乎所有服務,而不是創建自己的工廠。
public class SomeService : ISomeService
{
private readonly IFileSystem fileSystem;
public SomeService(IFileSystem fileSystem)
{
if (fileSystem == null)
throw new ArgumentNullException("fileSystem");
this.fileSystem = fileSystem;
}
public void DoSomething()
{
// The directory can be determined at runtime.
// It could, for example, be provided by another service.
string directory = @"C:\SomeWhere\";
// Create an instance of the DirectoryInfoWrapper concrete type.
DirectoryInfoBase directoryInfo = this.fileSystem.DirectoryInfo.FromDirectoryName(directory);
// Do something with the directory (it has the exact same interface as
// System.IO.DirectoryInfo).
var files = directoryInfo.GetFiles();
}
}
然後,您將配置容器只是爲了注入IFileSystem來獲得System.IO的所有功能。
IKernel ninject = new StandardKernel();
ninject.Bind<IFileSystem>().To<FileSystem>();