Hello All, welcome back. This post is part two of my articles on the SOLID principles. To quick recap, SOLID principles are a set of core practices that most software developers utilize to meet the demands of our clients in a way that is maintainable and easy to read.
Last time I talked about the SO of SOLID. And in that we discussed the idea of:
Single Responsibility Objects: Which is the idea that each class should have a single responsibility and should be developed to utilize those pieces.
Open / Closed Principle: Which is the idea that after a class is created, it is open to extension but closed to modification. Meaning that you can add to a class, inherit from that class, but you can't modify an existing class.
So moving on, the next part of the SOLID principles focuses on the Liskov Principle. Which is one that I know the name alone tends to be pretty off-putting. But really its a pretty basic idea. This all comes from a concept called programming to contracts. The idea is that if I have a class, say for example "Car", and then I have another class called "Honda", I should be able to substitute in "Honda" for "Car" in my code without making any configuration changes to the code that's calling it.
The principle actually describes it by saying that your subclasses should be able to replace your parent classes within your application and the code should not need further adjustment. One key point is that notice it does not say the reverse. Your subclasses, could potentially have additional methods that your parent does not, but at minimum the sub class should implement all the functionality of the parent class.
A real world example of this would be say a "ConfigurationProvider", for example I have in an application I'm working on a class called "ConfigurationProvider", and what it does is accepts a "Key" which is a string that denotes a record in the database configuration table. Now this works great for the WPF side of my application, but on the web, I wasn't too keen on reaching into the database this often for configuration values that aren't changing.
So I created a new class called "WebConfigurationProvider", and this inherits from ConfigurationProvider, and it stores the configuration in server cache after the first user calls for it, and if it can, it uses cache before reaching to the database.
Now according to Liskov's, I should be able to substitute "WebConfigurationProvider" in for anywhere I call the "ConfigurationProvider", and I can. Because they implement the same interface, IConfigurationProvider. This is a principle that's pretty easy to follow if you are entering any kind of Inversion of Control or Dependency Injection, as your coding to interfaces and should be inheriting your classes as per Open/Closed.
The "I" of SOLID stands for "interface segregation" principle. And this is one of those principles that tends to confuse people in my experience. People have a hard time wrapping their heads around. But really the easiest way I've found to describe this is its the "Single Responsibility" but for interfaces. C# is like a lot of languages out there in that it only supports "Single Inheritance". Meaning that you can only inherit from a single class. This logically makes things pretty easy to follow, and allows for the Liskov's principle to even be possible. Inheritance is a very powerful architectural tool in software, but its something that should be thought through.
Now interfaces on the other hand are a totally different story. Being that interfaces just provide contracts and no actual functionality means that the you can have as many interfaces as you want on a single class. The "Interface Segregation" principle, says that when you design your interfaces it should be with a single purpose in mind. The idea is that you utilize several interfaces to break up your methods logically, so that if you need to have separate implementations it is more easily done. For example, let's say you have a class named "PasswordProvider". And that class has the following methods:
- CreateRandomPassword: A method that generates a password when a user needs a random password.
- EncryptPassword: A method that encrypts the password before saying.
- DecryptPassword: A method that decrypts a user password.
- ValidatePassword: A method that validates a password to follow the rules required by the system.
Now based on dependency injection, or even just inversion of control, many of you would probably slap an "IPasswordProvider" on this, and be done. But the "Interface Segregation" Principle says differently. According to that principle we should put our interfaces to be single focused. I would recommend the following interfaces:
- IRandomPasswordGenerator: Has the single "CreateRandomPassword" method.
- IPasswordEncryption: Has the two methods of Encrypt and Decrypt.
- IPasswordValidator: has the validation method.
Some of you are probably looking at this and saying that's a needless headache. Now if I'm using DI I have to register 3 separate interfaces with the same class. And for what? Well, the benefit here is architectural flexibility. Right now it makes sense to you that a single class would solve all of these items. But that might not be true. Say for example, your clients come to you and say, they have a WCF service to handle the generation of random passwords. And they want you to use that. Under "Open/Closed" you need to inherit or build new. So you build a WCFRandomGenerator class, and implement the IRandomPasswordGenerator interface. Then update your DI container references and your done. You're architecture handles the change with minimum impact.
The idea is keeping your interfaces even more single purpose than your classes to allow your classes to evolve and your architecture to change over time.
The final principle I wanted to discuss here is the "D" in the SOLID principles, which is the "Dependency Inversion" principle. This is the one many people seem to understand pretty well at this point, thanks to the widespread use of Dependency Injection. Inversion of Control is nothing new, in fact if you look at the original papers on Object Oriented Programming it describes Dependency Inversion as the primary method of handling dependencies between classes. The idea here being that your classes should be written to utilize abstractions rather than concrete classes.
So that is our discussion of the SOLID principles, and next post we'll be taking a step back to look at some mobile development and utilizations of Xamarin and Xamarin Forms.