layout | title | date | author | lenght |
---|---|---|---|---|
post |
SOLID |
2019-10-06 |
Jonatas |
1 |
A class should have one, and only one, reason to change.
A Class should be responsible for a single task.
A Class should have only a single responsibility.
You should be able to extend a class’s behavior, without modifying it.
Open for extension but closed for modification.
A Class should be open to extension and close to modification.
Software entities … should be open for extension, but closed for modification.
Derived classes must be substitutable for their base classes.
A derived Class can be substituted at places where base Class is used.
Objects in a program should be replaceable with instances of their sub-types without altering the correctness of that program.
Make fine grained interfaces that are client specific.
Clients should not be forced to implement interfaces they do not use.
Don’t make FAT Interfaces. i.e. Classes don’t have to override extra agreements that are not needed for that Class simply because it is there in interface.
Many client-specific interfaces are better than one general-purpose interface.
Depend on abstractions, not on concretions. Not only high level Classes but low level Classes also depend on the abstractions in order to decouple the code.
- A. High level modules should not depend upon low level modules. Both should depend upon abstractions.
- B. Abstractions should not depend upon details. Details should depend upon abstractions.
But don't get confused with the terms:
- Dependency Injection,
- Dependency Inversion,
- Inversion of Control,
- IoC Container.
One should “depend upon abstractions, [not] concretions.”
If one class does more than one thing, it should be split into multiple classes. For example, an User class:
class User {
public bool validateUser(user){
...
}
public user formatUser(user){
...
}
public user getUserFromDb(id){
...
}
}
For example, an authentication module which requires username and password to login. You code a login module and perform login for that user.
class LoginModule
{
public bool loginUser(user)
{
if(normalUser) {
authenticateNormalUser(user);
}
else if (thirdPartyUser) {
authenticateGoogleUser(user);
}
}
}
In this case to add a new type of authentication, the original class has to change.
What to do?
Separate the login behaviour behind an interface and create separate Classes for normal login and third party login to flip the dependencies.
Any new person who wants to write a whole different login module functionality for a different set of users will create their own login module’s Class and implement the login interface (the functions that Login interface has as agreements).
interface LoginInterface
{
public AuthenticateUser(user);
}
class NormalLogin : LoginInterface
{
public bool AuthenticateUser(user)
{ ... }
}
class ThirdpartyLogin : LoginInterface
{
public bool AuthenticateUser(user)
{ ... }
}
class LoginModule
{
public bool login(user)
{
user.AuthenticateUser(user);
}
}
For example, a child class return a different type than its parent class.
It states that a client must not be forced to implement an interface that it doesn’t use. The result is fined grained interfaces
It states that High level modules should never depend on Low level modules, instead the High level module can depend upon an abstraction and the Low level module depends on that same abstraction.