Clean code and SOLID principles
Clean code and SOLID principles
Dear Engineers,
Your job is not to write code.
Clean code and SOLID principles
CORRECT
Clean code and SOLID principles
PERFORMANT
Clean code and SOLID principles

EASY TO READ
Clean code and SOLID principles

EASY TO MANTAIN
Clean code and SOLID principles

EASY TO EXTEND
Clean code and SOLID principles
TESTABLE/TESTED
Text

Clean code and SOLID principles
DOCUMENTED

"It is not the language that makes a program look simple, but the programmer who makes the language appear simple."
(quote from Robert C. Martin)
workaround = you haven’t spent enough time on trying to find a good, clean solution.
Good code is agnostic of language

KISS principle (Keep It Simple, Stupid!)
YAGNI principle (You Ain’t Gonna Need It).
Reading code = pleasure
Write code for humans
- don't write code for yourself
- don't write code for compilers
- don't be selfish
- don't torture other developer with hardly maintainable code
Code should be expressive
" What's in a name? that which we call a rose
By any other name would smell as sweet;"
(Romeo and Juliet)
Give good names
- should express their intention
- should not mislead you
- small documentation
IS THIS REALY TRUE ??

Don't_repeat_yourself : "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system"
Benefits:
- detect software vulnerability/bugs faster
- refactoring becomes very hard
- code is hard to understand/confusing
- facilitate code reuse
DRY - Code duplication
DRY - Code duplication
int a[] = new int[3];
a[0] = 1;
a[1] = 2;
a[2] = 3;
int b[] = new int[3];
b[0] = 5;
b[1] = 10;
b[2] = 15;
int sumA = 0;
for(int i=0; i< a.length; i++) {
sumA += a[i];
}
int averageA = sumA/a.length;
int sumB = 0;
for(int i=0; i< b.length; i++) {
sumB += b[i];
}
int averageB = sumB/a.length;
System.out.println(averageA);
System.out.println(averageB);

DRY - Code duplication
{
int a[] = new int[3];
a[0] = 1;
a[1] = 2;
a[2] = 3;
int b[] = new int[3];
b[0] = 5;
b[1] = 10;
b[2] = 15;
System.out.println(average(a));
System.out.println(average(b));
}
public static int average(int a[]) {
int sumA = 0;
for(int i=0; i< a.length; i++) {
sumA += a[i];
}
return sumA/a.length;
}

- Separate a computer program into distinct sections, each for each concern
- SoC well is called a modular program
- coupling/decoupling degree of each modules is very important
Examples:
- Internet protocol stack
- HTML, CSS, JavaScript
- Aspect-oriented programming (cross-cutting concerns: logging, caching) - (KISS applies also)
Separation of Concerns
- Layered designs are another embodiment of separation of concerns
- DAO - data access object, business logic layer, presentation layer
Separation of Concerns
public class UserDao {
public void getUserByCnp(String cnp) {
User user = ..
if(user.getAge() > 18) {
user.setCanDrink = true;
} else {
user.setCanDrink = false;
}
return user;
}
}

- we have side effect if the methods modifies some state outside its scope
- idempotent methods (applies also for 'state')
Side Effects
public static int calcPrintCube(int n) {
System.out.println(n * n * n); //Side effect
return n * n * n;
}
public static void main(String[] args) {
calcPrintCube(10);
int cube = calcPrintCube(20);
System.out.println(cube);
}

Nested if-else
public static int deleteUser(String user) {
return 0;
}
public static int deleteAccount(String user) {
return 1;
}
String user = "Bogdan";
if (deleteUser(user) == OK) {
if (deleteAccount(user) == OK) {
System.out.println("User deleted!!!");
}
else {
System.out.println("User deleted but account not!!!");
}
}
else {
System.out.println("User not deleted!!!");
}

Nested if-else
public static void deleteUserException(String user)
throws UserNotDeleted {
throw new UserNotDeleted();
}
public static void deleteAccountException(String user)
throws AccountNotDeleted {
throw new AccountNotDeleted();
}
....
{
String user = "Bogdan";
try {
deleteUserException(user);
deleteAccountException(user);
}
catch (UserNotDeleted userNotDeleted) {
System.out.println("User not deleted!!!");
}
catch (AccountNotDeleted accountNotDeleted) {
System.out.println("User deleted but account not!!!");
}
System.out.println("User deleted!!!");
}

SOLID
What are SOLID principles ?
- a way to follow POO pylons
SOLID
SRP
A class should have only a single responsibility
public class Vehicle {
private final int maxFuel;
private int remainingFuel;
public Vehicle(final int maxFuel) {
this.maxFuel = maxFuel;
remainingFuel = maxFuel;
}
// this is not a car's responsibility.
public void reFuel(){
remainingFuel = maxFuel;
}
public void accelerate() {
remainingFuel--;
}
}

SRP
A class should have only a single responsibility

public class FuelPump {
public void reFuel(final Vehicle vehicle){
int remainingFuel = vehicle.getRemainingFuel();
int additionalFuel = vehicle.getMaxFuel() - remainingFuel;
vehicle.setRemainingFuel(remainingFuel + additionalFuel);
}
}
OCP
Software entities … should be open for extension, but closed for modification.
public void changeDrivingMode(final DrivingMode drivingMode){
switch (drivingMode){
case SPORT:
vehicle.setPower(500);
vehicle.setSuspensionHeight(10);
break;
case COMFORT:
vehicle.setPower(400);
vehicle.setSuspensionHeight(20);
break;
default:
vehicle.setPower(400);
vehicle.setSuspensionHeight(20);
break;
// when we need to add another mode (e.g. ECONOMY)
2 classes will change DrivingMode and EventHandler.
}
}

OCP
Software entities … should be open for extension, but closed for modification.
public void changeDrivingMode(final DrivingMode drivingMode){
vehicle.setPower(drivingMode.getPower());
vehicle.setSuspensionHeight(drivingMode.getSuspensionHeight());
}
public class Comfort implements DrivingMode {
private static final int POWER = 400;
private static final int SUSPENSION_HEIGHT = 20;
@Override
public int getPower() {
return POWER;
}
@Override
public int getSuspensionHeight() {
return SUSPENSION_HEIGHT;
}
}

OCP
chain-of-responsibility pattern: different command objects and a series of processing objects

OCP
chain-of-responsibility pattern: different command objects and a series of processing objects
public class AccountsFilterChain {
protected List<AccountFilter> accountsFilters = new ArrayList<AccountFilter>();
public void filter (List<Account> accounts) {
for(AccountFilter accountFilter : accountsFilters){
accountFilter.filter(accounts;
}
}
}
OCP
chain-of-responsibility pattern: different command objects and a series of processing objects
public void handel(String requestName) {
if(requestName == "openAccount") {
OpenAccountHandler openAccountHandler = new OpenAccountHandler();
openAccountHandler.doJob();
}
if(requestName == "closeAccount") {
CloseAccountHandler closeAccountHandler = new CloseAccountHandler();
closeAccountHandler.doJob();
}
if(requestName == "debit") {
DebitAccountHandler debitAccountHandler = new DebitAccountHandler();
debitAccountHandler.doJob();
}
if(requestName == "credit") {
CreditAccountHandler creditAccountHandler = new CreditAccountHandler();
creditAccountHandler.doJob();
}
}
OCP
Sa scriem o aplicatie "money dispenser" = ATM
Avem suma de x lei pe care vrem sa o distribuim in bancnote de 10, 5 si 1 leu. Se va alege numarul maxim de unitati pentru bancnotele cu valoare mare.

LSP
Liskov substitution principle: objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.
If S is a subtype of T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties of T
Substitutability:
LSP
LSP is more than a IS-A relationship !!! It also requires that the Sub types must be substitutable for the Super class.
public class Animal {}
public class Deer extends Animal {}
......
Deer d = new Deer();
Animal a = d;
LSP
class Bird {
public void fly(){}
public void eat(){}
}
class Crow extends Bird {}
class Ostrich extends Bird {
fly() {
throw new UnsupportedOperationException();
}
}
public BirdTest {
public static void main(String[] args) {
List<Bird> birdList = new ArrayList<Bird>();
birdList.add(new Bird());
birdList.add(new Crow());
birdList.add(new Ostrich());
letTheBirdsFly ( birdList );
}
static void letTheBirdsFly ( List<Bird> birdList ) {
for ( Bird b : birdList ) {
b.fly();
}
}
}
LSP
class Super {
Object getSomething(){}
}
@Override
class Sub extends Super {
String getSomething() {}
}
.................
String s = "bogdan";
Object test = null;
test = s;
class Super{
void doSomething(String parameter)
}
class Sub extends Super{
void doSomething(Object parameter)
}
Covariance
Contravariance
No new exceptions should be thrown by methods of the subtype
LSP
https://stackoverflow.com/questions/56860/what-is-an-example-of-the-liskov-substitution-principle/8279878

ISP
Many client-specific interfaces are better than one general-purpose interface
public interface Switches {
void startEngine();
void shutDownEngine();
void turnRadioOn();
void turnRadioOff();
void turnCameraOn();
void turnCameraOff();
}
public abstract class Vehicle implements Switches {
private boolean engineRunning;
public boolean isEngineRunning() {
return engineRunning;
}
public void startEngine() {
engineRunning = true;
}
public void shutDownEngine() {
engineRunning = false;
}
}
ISP
public class Car extends Vehicle {
public void turnRadioOn() {
}
public void turnRadioOff() {
}
public void turnCameraOn() {
// nothing to do here
}
public void turnCameraOff() {
// nothing to do here
}
}
public class Drone extends Vehicle {
private boolean cameraOn;
public boolean isCameraOn() {
return cameraOn;
}
public void turnCameraOn() {
cameraOn = true;
}
public void turnCameraOff() {
cameraOn = false;
}
public void turnRadioOn() {
// nothing to do here
}
public void turnRadioOff() {
// nothing to do here
}
}
DIP
"one should depend upon abstractions, not concretions"
DIP


Recommandations

Conclusions
- is more than writing code
- writing code that works is not the hardest part
- trivial things and natural one are very different
- develop your own personality :)
THANKS !!!
CLEAN CODE
By Bogdan Posa
CLEAN CODE
- 1,564