SOLID approach to development - dime-worldbank/Disease-Modelling-SSA GitHub Wiki

SOLID code

SOLID is a code design principle that aims to a code base maintainable, more readable and easier to extend and develop features in the future.


S - single responsibility principle. A class should have one and only one reason to change.

In your code, there should be a place for everything and everything should be in it's place. Meaning that if you had for example, a class which was responsible for producing output files (in this codebase this is handled by Logging.java), then the only part of your code base that should produce an output file is the class responsible for producing output files.

Following from this, if you then wanted to create an additional output file or edit an existing one, you would know exactly which file to look at in order to make your changes.

This does not only apply to classes, the same principle will also apply to methods/functions.


O - open/closed principle. An entity should be open for extension but closed for modification.

If you want to develop a new feature in the code base, you should aim to create new code which can be separated from the current code base, rather than editing the existing code base. This practice will ensure that the existing functionality of your code is preserved.

This principle helps develop a stable code base through a process of testing the code's existing functionalities with each additional change made to the code base.


L - Liskov substitution principle. Let f(x) be a property provable about objects x of type T. Then f(y) should be provable for object y of type S where S is a subtype of T.

"A subtype should behave as a supertype as far as you can tell using the supertype methods. It's not that the subtype can't behave differently, but as long as you limit your interactions to the supertype methods you would get the behaviours you expected." - Liskov herself, see https://www.youtube.com/watch?v=-Z-17h3jG0A

In our context we can explain this with an example. Say that we developed another disease model as part of this project, for example Tuberculosis. Both diseases could be represented in s SEIR modelling framework, however despite their similarities we would expect differences in health outcomes (i.e. death rates) and even behaviours (for example quarantining after a positive COVID test).

Both diseases would make use of the susceptible, exposed, infected, recovered and death nodes that exist in the model, borrowed from a disease supertype. The differences in the diseases would be attributable to the disease subtypes.


I - interface segregation principle. No code should be forced to depend on methods it does not use.

Larger interfaces should be split into smaller, specific ones so that the user will only have to use the interfaces and inherited methods that are relevant to what they intend to do with the code.

For example, imagine we wish to model two diseases in a population. One disease is communicable, meaning that it is spread from person to person (for example COVID). The other disease in non-communicable, meaning that infection cannot pass from one person to the other (for example cancers). Both diseases may share disease states, such as being susceptible, infected and passing away from their respective diseases, so we could design interfaces that both these disease could make use of. But we shouldn't represent the non-communicable disease with a communicable disease framework as an infect other people method would not be used.


D - dependency inversion principle. High level modules should not depend on low level modules. Both should depend on abstractions.

When we have one section of the code interacting with and making changes to another (for example, infections interacting with people). We can end up developing rigid code.

For example, in the current state of the model, the infection class is tailored only to COVID infections. If we were to include a new disease, we could not make use of the model's existing infectious behaviour nodes as the progression from state to state is currently written such that it only applies to COVID infections. To make the codebase flexible and easier to include more diseases in the future, we could develop abstractions of commonly used methods (for example spreading the disease from person to person, or dying from a diseased) that could be used by multiple diseases.