I evaluate the points of using dependencies through an intent / mechanism lens: does this code really convey its intentions or do I need to extract it from a bunch of implementation details?
If the code really looks like a bunch of implementation details, I define the input and output data and create a completely new dependency to imagine why there is all this. Then I expose complexity to a new dependency, making the source code simpler and more understandable.
When I read the code in this question, I clearly see a search for the file path based on the date, followed by an opaque set of statements that do not explicitly tell the purpose of reading an object of a certain type in a specific path. I can break through it, but it breaks my step.
I suggest that you increase the abstraction level of the second half of the calculation after you get the path. I would start by defining a dependency that implements the code inputs / outputs:
public interface IEntityReader { IEnumerable<T> ReadEntities<T>(string path); }
Then rewrite the source class using this interface showing the intent:
public sealed class CompanyDetailProvider : ICompanyDetailProvider { private readonly IFilePathProvider _filePathProvider; private readonly IEntityReader _entityReader; public CompanyDetailProvider(IFilePathProvider filePathProvider, IEntityReader entityReader) { _filePathProvider = filePathProvider; _entityReader = entityReader; } public IEnumerable<CompanyDetail> GetCompanyDetailsForDate(DateTime date) { var path = _filePathProvider.GetCompanyDetailsFilePathForDate(date); return _entityReader.ReadEntities<CompanyDetail>(path); } }
Now you can add gory details to the sandbox that become pretty cohesive individually:
public sealed class EntityReader : IEntityReader { private readonly IDataReaderFactory _dataReaderFactory; public EntityReader(IDataReaderFactory dataReaderFactory) { _dataReaderFactory = dataReaderFactory; } public IEnumerable<T> ReadEntities<T>(string path) { Func<IDataReader> sourceProvider = () => _dataReaderFactory.CreateReader(DataFileType.FlatFile, path); return new Hydrator<T>(sourceProvider); } }
As shown in this example, I think you should distract the factory data reader and create a hydrator directly. The difference is that EntityReader uses a factory data reader, while it creates a hydrator. In fact, it is instance independent; instead, it serves as a factory hydrator.
Bryan watts
source share