In the previous post we looked at letter ‘L’ in SOLID, i.e. the Liskov Substitution Principle. We saw how adhering to the LSP helps remove implementation-specific details from a client class that consumes those implementations. The client class will be able to consume the services of an abstraction without worrying about the actual implementation of the abstraction. The goal of LSP is that any implementation of an abstraction will work seamlessly within the consuming class.

We looked at the following cases that can indicate a violation of LSP:

switch or if-else blocks checking for the value of an enumeration

Code blocks that check the actual type of an abstraction to branch logic. This can be coupled with downcasting the abstraction to a concrete implementation type

Blocks with magic strings that check for a value in some string parameter and branch logic accordingly

There’s at least one more indicator which we haven’t seen so far. I decided to devote a separate article to that case as it brings us closer to the next constituent of SOLID, i.e. the Interface Segregation Principle.

Imagine that the requirements have changed. The implementation of the OnDemandAgentService.StartNewOnDemandMachine() method must be extended in a way that unauthorized users must be locked out of the user store. Furthermore, the company wants to store all users in an SQL Server DB.

By now we know that hiding concrete implementations behind abstractions can be beneficial so we’ll take that approach here:

So this works well for the time being. However, we don’t want to forget about our previous authorization service. We want to be able to use that as well within OnDemandAgentService. However, it doesn’t have any user store so a lockout logic is not really applicable to it:

If we set this implementation through the AuthorizationService property setter of OnDemandAgentService then StartNewOnDemandMachine will stop working when calling…

bool lockedOut = _authService.LockOut(Username);

…since it throws a not implemented exception. We’re clearly violating LSP. The client object cannot simply consume the abstraction like it did before. We’re degrading the functionality as soon as we pass in the AuthorizationService concrete implementation for the IAuthorizationService dependency.

An attempt to remedy the situation is to implement the interface in a way that the exception is not thrown:

public bool LockOut(string username)
{
return true; //or false
}

However, that would raise subtle bugs. It returns true every time and then we may be wondering why the same user is still allowed to perform other operations in the system. If, on the other hand, it returns false all the time then we might go look for the error why the user couldn’t be locked out. Hence we introduced some bogus behaviour in our implementation just to avoid throwing an exception somewhere in the system. We’re effectively forcing AuthorizationService and DatabaseAuthorizationService to be on the same level of the “IS-A” relationship. DatabaseAuthorizationService definitely satisfies the “IS-A” relationship, i.e. a DatabaseAuthorizationService is an IAuthorizationService because it can unambiguously fulfil the IAuthorizationService contract. AuthorizationService can only partially do that so we can say the AuthorizationService “IS-NOT-REALLY-A(N)” IAuthorizationService.

What can we do then? One obvious solution is of course to introduce some kind of backing store for the users in AuthorizationService so that the LockOut method makes sense for it.

Another solution is to break up the IAuthorizationService interface according to the next constituent of SOLID, which is the Interface Segregation Principle. More on that in the next post.