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,497