1

Contrapositive-ish question: What is the design reason a library class would not implement an interface as a convenience to the developer? (e.g. the .Net library class FileInfo)


The FileInfo class necessarily is dependent on the file system. This obviously present problems when unit-testing. If I had to design and implement this class for my own internal use, I would get around the issue by implementing FileInfo as a matching IFileInfo. Then I can inject the IFileInfo dependency however I want and unit-test dependent code easily.

But I don't implement FileInfo myself, I get it from a 3rd party, so I have to create an interface, a wrapping class, and somehow stub or mock that interface to achieve a similar result.

It seems to me that providing the interface as part of the library is a thoughtful way of making the future developer's life easier, and their code easier to maintain.

Would that be an acceptable way to design a library, why or why not?

3
  • 2
    I would only provide an interface if I wanted the caller to be able to provide there own implementation. In most cases libraries provide concrete implementations not abstractions. As you have said, you could provide your own FileInfo interface so your could abstract it away during testing if needed.
    – Jon Raynor
    Oct 10, 2018 at 18:28
  • That's a good point. I do in a way expect the caller to be able to provide their own implementation in the form of a stub or a mock. However, I wouldn't want anyone to replace my concrete class with their own implementation to use in production code.
    – etberg
    Oct 10, 2018 at 18:51
  • FileInfo has 22 public methods/properties. Providing an interface with all those signatures for mocking "just in case" is probably not a really good idea.
    – Doc Brown
    Oct 11, 2018 at 6:17

2 Answers 2

3

An interesting take on that comes from considering what we learned of modules from Parnas. If modules are designed to be decision boundaries, and choosing to satisfy our requirements via FileInfo is a decision we are making, then there should be a small boundary module around that decision.

So the interface we want for testing is the boundary module, not FileInfo.

The code smell you are getting from your test perhaps should challenge the choice to expose FileInfo in your API.

1
  • So it sounds like the way I assume 3rd party libraries will be used is flawed. At the very least, there is more than one approach when consuming them. In my case, if I want to use FileInfo and unit test it, I wouldn't want to explicitly wrap it, but abstract what I am trying to accomplish with it. Is that what you are saying?
    – etberg
    Oct 11, 2018 at 16:05
3

One thing to bear in mind with FileInfo, is that it's both a data object and a service in one.

For objects, I wouldn't normally create interfaces (the main reason I ever create an interface for data objects is mainly to allow for a readonly version of it).

Services that you want to be able to stub out would need an interface.

If it was up to me, I would not use FileInfo the way you described. I would separate it into an object and a service as 2 separate concepts. Inject the service into the classes that need to act upon the object, where the object can just be a string (the filepath).

Simply pass the filepath as string around and feed it to your service whenever you want to operate on the filepath itself.

2
  • That's an interesting way of looking at it. I haven't thought about it, but I naturally avoid interfaces on data objects as well. How would you define a "Service" exactly in this context? A class that works with external resources?
    – etberg
    Oct 11, 2018 at 16:09
  • In short, a service simply contains the logic to perform actions. e.g. IPersist, exposing void Store(Model model) and Model Load(). Then you can have different implementations for it, e.g. FilePersist and CloudStoragePersist. Where as Person, would be an object, simply storing attributes about the person.
    – z0mbi3
    Oct 12, 2018 at 22:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.