My understanding of interfaces is that they define contracts and classes implementing the interface sign the contract. In that case, we may design classes to depend on the contract and not on the concrete implementation. This has advantages like reducing coupling, enabling polymorphism, and so forth.
Now I have came across one use of interfaces which I didn't get. This is from the ASP.NET Core documentation, regarding configuration. In particular, in the page, one is talking about setting up MongoDB with ASP.NET Core.
They basically define a class and an interface like this:
namespace BooksApi.Models
{
public class BookstoreDatabaseSettings : IBookstoreDatabaseSettings
{
public string BooksCollectionName { get; set; }
public string ConnectionString { get; set; }
public string DatabaseName { get; set; }
}
public interface IBookstoreDatabaseSettings
{
string BooksCollectionName { get; set; }
string ConnectionString { get; set; }
string DatabaseName { get; set; }
}
}
Then they use this as follows:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<BookstoreDatabaseSettings>(
Configuration.GetSection(nameof(BookstoreDatabaseSettings)));
services.AddSingleton<IBookstoreDatabaseSettings>(sp =>
sp.GetRequiredService<IOptions<BookstoreDatabaseSettings>>().Value);
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
I must say I gon't get it. The objects of type BookstoreDatabaseSettings
are meant to work as DTOs for the settings. They don't provide any functionality at all. So why introduce one interface in the middle?
I don't see here one usage on which one would use some other implementation. I don't see here why one is trying to decouple this, I don't see any use of polymorphism at all, and I really didn't get the point.
So why in this situation, when dealing with settings in ASP.NET Core, one uses interfaces and not the concrete class (for example BookstoreDatabaseSettings
) directly?
If you want to introduce an abstraction for multiple specific things. If you want to treat different, specific classes in some way that is the same for every one of them, you should introduce an interface that covers their common ground.
An interface defines common functionality across unrelated classes. For example, all sorts of classes that look nothing like each other may have the need to safely get rid of the resources they use.
Interfaces can be implemented by multiple classes. There is no rule that only one class can implement these. Interfaces provide abstraction to the java.
An interface can be used to define a contract behavior and it can also act as a contract between two systems to interact while an abstract class is mainly used to define default behavior for subclasses, it means that all child classes should have performed the same functionality.
In the context of just one app, you're correct that this doesn't make sense. However, there could be scenarios where it might.
What's happening here is that, first, the BookstoreDatabaseSettings
is bound to the config section. This is technically all that's required. However, this uses the options pattern, which mean that you must then injnect IOptions<BookstoreDatabaseSettings>
, IOptionsSnapshot<BookstoreDatabaseSettings>
etc., rather than BookstoreDatabaseSettings
directly. That implies a dependency on Microsoft.Extensions.Options
, which is not necessary a bad thing, but it is a dependency nonetheless.
The next line then binds the interface to the actual BookstoreDatabaseSettings
instance, pulled from IOptions<BookstoreDatabaseSettings>
. In other words, you can now simply inject the interface, instead of IOptions<BookstoreDatabaseSettings
. Again, nothing monumental, but it abstracts the use of Microsoft.Extensions.Options
. For what it's worth, you could have just as easily registered BookstoreDatabaseSettings
instead of the interface to get the same thing. Then, you'd be able to inject BookstoreDatabaseSettings
directly, without a dependency on Microsoft.Extensions.Options
.
What the interface buys you here, is abstracting the actual class implementation. You could potentially put the interface alone in a library that's shared between projects, and leave the actual class implementation to each individual project. If your shared libraries depend only on the interface, then the actual implementation that's provided won't matter. However, you could just as easily share the class implementation between projects as well. It's just a matter of perspective.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With