Writing SOLID code
Reference
- Singleton
- Tight Coupling
- Untestability
- Premature Optimization
- Indescriptive Naming
- Duplication
Singleton
- Anti-pattern
- Uses global state
- Hidden dependencies
Tight Coupling
If making a change to one module/component requires a change in another -
coupling exists!
Untestability
If writing tests is hard, it means that the code is bad!
Tight coupling!
Premature Optimization
... is the root of all evil. There is only cost and no benefit.
Donald Knuth
Indescriptive Naming
Programming languages are for humans.
Don't abbreviate!
Duplication
Don't Repeat Yourself (DRY)
- Single Responsibility Principle
- Open/Closed Principle
- Liskov Substitution Principle
- Interface Segregation Principle
- Dependency Inversion Principle
Single Responsibility Principle
Just because you can add everything you want into your class doesn’t mean that you should.
Open/Closed Principle
Software entities (classes) should be open for extension, but closed for modification.
Liskov Substitution Principle
Objects should be replaceable with instances of their subclasses without altering the correctness of the program.
Interface Segregation Principle
Many specific interfaces are better than one general-purpose interface.
Dependency Inversion Principle
Abstractions should not depend upon details
Details should depend upon abstractions.
Dependency Inversion Principle
Dependency Inversion Principle
Object Calisthenics
A set of rules (recommendations) how to write better object oriented code.
1. One level of indentation per method
Too many levels of indentation =
bad readability
Hint: Split the method into several smaller ones.
// Before
function validateCollection() {
this.collection.forEach(function (item) {
item.forEach(function (property) {
if (typeof property !== 'string') {
throw new Error('invalid');
}
});
});
return true;
}
// After
function validateCollection() {
this.collection.forEach(function (item) {
this.validateItem(item);
});
}
function validateItem(item) {
item.forEach(function (property) {
this.validateProperty(property);
});
}
function validateProperty(property) {
if (typeof property !== 'string') {
throw new Error('invalid');
}
}
2. Don't use the "else" keyword
Nested and branching conditionals =
bad readbility
Hint: Early return.
function validateProperty(property) {
if (property.isString()) {
if (property.equals('invalid')) {
return false;
} else {
return true;
}
} else {
throw new Error('Not a string');
}
}
function validateProperty() {
if (!property.isString()) {
throw new Error('Not a string');
}
if (property.equals('invalid')) {
return false;
}
return true;
}
2. Don't use the "else" keyword
3. Wrap all primitives and strings
If it has a behaviour, encapsulate.
3. Wrap all primitives and strings
var date = '2016-05-31';
// 120 bucks
var money = 120;
// mph
var velocity = 80;
var date = new Date('2016-05-31');
var money = new Money(120, 'USD');
var velocity = new Velocity(80, 'mph');
velocity.increaseBy(20);
velocity.decreaseBy(150); // --> error, cannot be negative
4. First class collections
Encapsulate a set of elements in an object and add specific behaviours (methods).
First class collections
// Array
var userData = [
// ...
];
var user = null;
for (var i = 0; i < userData.length; i++) {
if (userData[i].username === 'testuser') {
user = userData[i];
break;
}
}
// Collection
var users = new UserCollection(userData);
users.count();
users.findByUsername('testuser');
5. One dot per line
- Law of Demeter: Don't talk to strangers.
- Doesn't apply for "fluent interfaces".
var totalMembers = 0;
users.getUser('testuser').getGroups().forEach(function(group) {
totalMembers += group.getMembers().count();
});
// User
function getTotalGroupMembers() {
return this.getGroups().getTotalMembers();
}
// Group collection
function getTotalMembers() {
var number = 0;
this.forEach(function(group) {
number += group.getMemberCount();
});
return number;
}
// Group
function getMemberCount() {
return this.members.count();
}
5. One dot per line
Don't abbreviate
Write code for humans, not machines.
Naming!
7. Keep all entities small
- Original rule: No class over 50 lines of code.
- For JavaScript: Soft limit of about 50-150 lines without the comments.
8. Instance variables limit
- Original rule: No more than two instance variables per class.
- JavaScript: Soft limit of around 5 instance variables.
9. No getters and setters
Implement behaviours instead of exposing class data through getters/setters --> better encapsulation.
9. No getters and setters
var account = new Account(120);
var addedAmount = 30;
var currentAmount = account.getBallance();
account.setBallance(currentAmount + addedAmount);
// instead:
account.addAmmount(30);
Writing SOLID code
By Ivan Novakov
Writing SOLID code
- 1,116