SOLID is a mnemonic for five design principles intended to make your code more flexible, maintainable, and reusable.
Single Responsibility Principle:
A class should be responsible for a single functionality of your code. Therefore a Class should have only one reason to change.
Without applying the SRP, we risk breaking the working code while modifying other functions that don’t belong in the Document class.
A class should open for extension but closed for modification. We should always aim to write a Class in which we can extend its behavior on-demand and not modify its code repetitively.
We shouldn’t modify the Shipping Cost function and the Order class every time we add a shipping method. Think about this principle when you have multiple If/Switch statements.
Liskov Substitution Principle:
- Covariance: Assigning a more derived type to a less derived type. For example : IEnumerable<string> s = new List<string>();
- Contravariance: Assigning a less derived type to a more derived type. For example :
Action<string> act_s =act_o
Defined by Barbara Liskov in 1987. This principle is a set of rules to be respected while subclassing.
- There must be contravariance of the method arguments Types in the subtype. (or preserve the types)
- There must be covariance of the return Types in the subtype. (or preserve the types)
- A method of a subclass should not throw types of exceptions that the base class method doesn’t throw.
Lucky for you, Statically typed programming languages have these rules built-in. They force you to apply these rules :).
- Pre-conditions, cannot be strengthened in a subtype. For example: if the base class requires an integer for a function, the overridden method cannot require that the integer is negative.
- Post-conditions cannot be weakened, in a subtype.
- Invariants of a supertype must be preserved in a subtype. For example, A dog has four legs…always!
Interface Segregation Principle
Make your interfaces as compact as they can be, do not add behaviors that a client class may not use and/or doesn’t have the means to implement.
Dependency Inversion Principle
- Low-level modules implement basic operations such as interacting with a disk, connecting to a database, etc…
- High-level modules contain complex logic that orders low-level classes to perform a certain task.
High-level modules should not depend on low-level modules. Both should depend on abstractions(interfaces).
Making high-level classes dependent on interfaces is much softer than depending on low-level classes. Implementing the interfaces for low-level classes makes them depend on the business logic reversing the direction of the original dependency.
Great to see you have come this far, and remember practice makes perfect!