Make fine grained interfaces that are client specific.
Robert C. Martin
Welcome back to the series on SOLID. By now, I’ll assume you’ve read my earlier post on the Liskov Substitution Principle. The guts of this principle is that your interfaces should be very specific to the purpose. You shouldn’t have a super interface or class that does everything. The method that you’re going to send that class to will only need one slither, one tiny piece of what you’re going to give it. If you send the whole thing you’ll end up with:
- A confused developer. What’s the property or method that I need?
- God classes. I mean, you’re already sending in everything – why not send more.
Breaking the principle
So, to break this principle, I’m going to show you an example of an interface that could be broken up. Let’s talk real – we all have Repositories, right? Maybe you decide to have all your repository calls in one class. Makes things simple. You only have one file to worry about.
public class Product { public Guid Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } } public class Warehouse { public Guid Id { get; set; } public string Location { get; set; } public int NumberOfProducts { get; set; } public int MaxNumberOfProducts { get; set; } } public class DeliveryVan { public Guid Id { get; set; } public string Location { get; set; } public int MaxNumberOfProducts { get; set; } public bool HasDriver { get; set; } } public interface IDatabaseRepository { Product GetProduct(Guid id); Product GetProductByName(string name); void SaveProduct(Product product); IEnumerable<Product> GetAllProducts(); Warehouse GetWarehouse(Guid id); Warehouse GetWarehouseByLocation(string location); void SaveWarehouse(Warehouse warehouse); IEnumerable<Warehouse> GetAllWarehouses(); DeliveryVan GetDeliveryVan(Guid id); DeliveryVan GetDeliveryVanByLocation(string location); void SaveDeliveryVan(DeliveryVan warehouse); IEnumerable<DeliveryVan> GetAllDeliveryVans(); }
But, you can see that it’s starting to get big. Now when people want database access, how do they know what they want? How do you know that they won’t do something stupid?
Fix using the principle
Now we know that we should have client-specific interfaces – with the example above we should make domain-specific interfaces. We have a clearly defined set of domains in this case. The product, the warehouse, and the delivery van are the three domains. Now, it’s not always so simple. What happens if your warehouse references products? In such a case, you would decide for yourself which domain it would belong to.
public class Product { public Guid Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } } public class Warehouse { public Guid Id { get; set; } public string Location { get; set; } public int NumberOfProducts { get; set; } public int MaxNumberOfProducts { get; set; } } public class DeliveryVan { public Guid Id { get; set; } public string Location { get; set; } public int MaxNumberOfProducts { get; set; } public bool HasDriver { get; set; } } public interface IProductRepository { Product GetProduct(Guid id); Product GetProductByName(string name); void SaveProduct(Product product); IEnumerable<Product> GetAllProducts(); } public interface IWarehouseRepository { Warehouse GetWarehouse(Guid id); Warehouse GetWarehouseByLocation(string location); void SaveWarehouse(Warehouse warehouse); IEnumerable<Warehouse> GetAllWarehouses(); } public interface IDeliveryVanRepository { DeliveryVan GetDeliveryVan(Guid id); DeliveryVan GetDeliveryVanByLocation(string location); void SaveDeliveryVan(DeliveryVan warehouse); IEnumerable<DeliveryVan> GetAllDeliveryVans(); }
I for Interface Segregation Principle
This principle is about making sure you don’t have large interfaces that no one can understand. You want to make them as specific as possible so that classes cannot run and do not run the code you don’t want them to. You want to make developers lives easier by making it obvious what they should call. Remember, be specific and keep it small. Also: don’t go overboard and create an interface per method. You don’t want to go in the other direction also.
Leave a Reply