As a company over the past few years, we have been adapting our coding style to keep in step with best practises adopted by other developers in the industry. This is helping us to produce far more efficient, modular code which is much more extensible and reusable.
Using an example of a Kentico form control I developed, I will demonstrate some of the benefits of adhering to SOLID principles.
The Form Control
Recently, we have been developing sites which make use of Font Awesome. Font Awesome allows us to add scalable vector icons to websites which can be customised via CSS. In order to give our clients the ability to add content icons on some of their pages, I've created a Kentico form control which brings the power of Font Awesome into the CMS. It contacts Font Awesome’s live icon YAML feed; parses the response stream into a list of generic icon objects; and then allows the list to be filtered.
So what is "SOLID"?
SOLID is an acronym of best practises a developer should follow in their day to day work. It stands for:
- Single responsibility principle
- Open-closed principle
- Liskov substitution principle
- Interface segregation principle
- Dependency inversion principle
In order to demonstrate the above ideas, I'll describe how they've been implemented into the icon selector code base and the advantages they provide.
Single Responsibility Principle
This states that a class should have one, and only one reason to change. This can be inferred to state that each class should be responsible for one overall action, and any other actions should be separated into an additional classes.
The icon selector achieves this by having four separate classes to retrieve the icon set to send to the Kentico form control:
- IconSearch - Responsible for searching through the icons found after having user input from the form control
- FontAwesomeParser - Responsible for parsing a Yaml stream into a generic list of icons
- IconWebFetcher - Responsible for retrieving a stream from a remote web location
- FontAwesomeApplicationSettings - Responsible for retrieving settings from the application such as where the remote YAML feed can be found
This result here is a much more modular, cleaner code-base. If any new developments happen, they are unlikely to affect any other part of the application as each change happens in separation.
The second of the SOLID principles states that a class should be open for extension, but closed for modification. This means that functionality can be added (or extended) by adding new classes, but code in existing classes should never be modified, except to fix bugs.
In order to satisfy this principle, an interface has been created for each of the classes mentioned above:
- IIconParserSettings & IIconFetcherSettings
This has allowed our classes to depend on abstract base classes instead of concrete implementations. The instantiating class is unaware and in fact doesn't need to know about how its dependencies are implemented, allowing us to extend the functionality provided by applying a new implementation which doesn't require a change to be made to the instantiating class. This goes hand in hand with the single responsibility principle and ensures everything remains modular and changes are made in separation.
Liskov Substitution Principle
The third SOLID principle was introduced by Barbara Liskov in 1987 and states that functions that use pointers of references to base classes must be able to use objects of derived classes without knowing it. Or in simpler terms - subtypes must be substitutable for their base types.
In our example, we should be able to substitute the IconWebFetcher which implements the IIconFetcher interface for a new implementation of IconFileFetcher, which will return a stream from a file instead of from a remote web location without adversely affecting the system.
This extends (ignore the pun!) the open-closed principle to ensure that when extra functionality is added, the results are still expected, and allows us to not only request our FontAwesome YAML feed from a different location, but it also allows us to request a different icon set altogether - Fontello for example!
Interface Segregation Principle
The penultimate solid principle SOLID principle states that no client should be forced to depend on methods it does not use.
This can most easily be demonstrated through my use of it in our functional implementation. You may have noticed that in the explanation of the open-closed principle, I split the implementation of the FontAwesomeApplicationSettings into two base classes - IIconParserSettings & IIconFetcherSettings? This was to satisfy the interface segregation principle. Both the FontAwesomeParser and IconWebFetcher classes require the use of settings. Since it wouldn't make sense to allow the parser class access to the settings it has no need for (the remove HTTP location for example), the settings class has been split in two and the relevant classes use only the specific base classes they require.
Dependency Inversion Principle
The final SOLID principle states that high level modules should not depend upon low level modules – both should depend on abstractions, and that abstractions should not depend upon details – details should depend on abstractions.
My icon selector implementation has each of the classes constructor allow its dependencies to be passed to it. The means that the implementation only expects an abstracted version of a class – it is always unaware of the specific implementation it uses as it never creates it itself.
Again, this promotes better modularisation and ease of testing has all of a classes dependencies are abstract and can therefore be mocked in any unit testing that is required.
Hopefully I've provided enough information to show how to implement the SOLID principles and the advantages of doing so. Having at least a basic grasp of these techniques is an important first step before using more advanced unit testing and the dependency injection design pattern.
Although examples have been shown in this blog post, please see below for the full skeleton sample code:
The icon selector I have developed is shown below in its FontAwesome form, but is flexible enough to produce a list of icons from any source of CSS scalable vector icons. It is available by request with any NetConstruct developed Kentico site, version 8.0 and newer.