What are you looking for in a dependency to determine if it should be an injected dependency?

I find it difficult to understand when to add dependency. Let me just work with a simple example from my project:

class CompanyDetailProvider : ICompanyDetailProvider { private readonly FilePathProvider provider; public CompanyDetailProvider(FilePathProvider provider) { this.provider = provider; } public IEnumerable<CompanyDetail> GetCompanyDetailsForDate(DateTime date) { string path = this.provider.GetCompanyDetailFilePathForDate(date); var factory = new DataReaderFactory(); Func<IDataReader> sourceProvider = () => factory.CreateReader( DataFileType.FlatFile, path ); var hydrator = new Hydrator<CompanyDetail>(sourceProvider); return hydrator; } } 

(Not product quality!)

ICompanyDetailProvider is responsible for providing instances of CompanyDetail to consumers. The specific implementation of CompanyDetailProvider does this by hydrating instances of CompanyDetail from a file using Hydrator<T> , which uses reflection to populate T instances obtained from IDataReader . Clearly, CompanyDetailProvider dependent on DataReaderFactory (which returns OleDbDataReader instances with the file path) and Hydrator . Should these dependencies be introduced? Is entering FilePathProvider ? What qualities do I check to decide whether to introduce them?

+6
c # dependency-injection
source share
3 answers

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.

+2
source share

How to determine if a class should use dependency injection


Is an external dependency required for this class?

If yes, enter.

If not, it has no dependency.

Reply "Is entering FilePathProvider correct?" Yes, that's right.

Edit:. To find out any external dependency, you call an unrelated, but dependent class, especially when it comes to physical resources, such as reading files from disk, but this also implies any service or model class that makes the logic independent of the main functionality of the class.

As a rule, this can be assumed at any time when you call a new operator. In most cases, you want to reorganize all uses of the new operator when it has to deal with any class other than the data transfer object. When the class is internal to the location of use, the new statement can be accurate if it reduces complexity, such as the new DataReaderFactory (), however it is also a very good candidate for embedding the constructor.

+1
source share

I am prone to the more liberal side of injection dependencies, so I definitely want to insert both IDataReader to get rid of the new DataFactoryReader and Hydrator. He keeps more and more weakly connected, which, of course, facilitates maintenance.

Another advantage that is easy to achieve right away is better testability. You can create mocks of your IDataReader and Hydrator to isolate your unit tests only with the GetCompanyDetailsForDate method and not worry about what happens inside the datareader and hydrator.

+1
source share

All Articles