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 네임 스페이스의 유형이 느슨하게 결합 된 방식으로 제공되는 많은 서비스를 제공합니다.
그래서 자신의 공장을 만드는 대신 System.IO 네임 스페이스가 제공하는 거의 모든 서비스에 대한 액세스 권한을 얻기 위해 기존의 IFileSystem 서비스를 삽입 할 수 있습니다.
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>();