Sunday, April 18, 2021

Cohesion and coupling of an object in OO programming

I keep seeing developers extracting coupled logics into new classes, which reduces the cohesion of objects and makes objects tightly coupled to each other. This article describes a way to check the cohesion of the code in an object and the coupling between objects, which helps developers check whether the refactoring improves the cleanness of the code or not.

Please note that it can not be applied to pure functional programming.

Cohesion

VF = number of private fields (no non-private getter or setter, not a property)
OF = number of non-private fields
U = number of usage of fields by public methods (count 1 per public method per field)
M = number of public methods

C = cohesiveness (0 to 1, 0 means the worst, 1 means the best)

C = U / ((M + OF) * (VF + OF))

Other rules:

  • protected counts as private
  • Calculation includes all fields and methods in the parent classes (except Object) 

Coupling/Independence

F = number of fields (both private and non-private) as:
  F = VF + OF

R = number of references

P = coupling factor (0 to 1, 0 means no coupling, 1 means fully coupled to others)

P = R / F

I = factor of independence
I = 1 - P

Examples:

1. Full cohesion:

public class Example {
    private final String value; // VF: +1

    public Example(String value) {
        this.value = value;
    }

    public int m1() { // M: +1
        return value.length(); // U: +1
    }
}

// C = 1/((1 + 0) * (1 + 0)) = 1

2. No cohesion:

public class Example {
    private final String value; // OF: +1

    public Example(String value) {
        this.value = value;
    }

    public String getValue() { // M: does not count
        return value;  // U: does not count
    }
}

// C = 0/((1 + 0) * (1 + 0)) = 0

3. Half cohesion:

public class Example {
    private final String value; // OF: +1

    public Example(String value) {
        this.value = value;
    }

    public String getValue() { // M: does not count
        return value;  // U: does not count
    }
    
    public int m1() { // M: +1
        return value.length() + value.indexOf("a"); // U: +1
    }
}

// C = 1/((1 + 1) * (1 + 0)) = 0.5

4. Coupling:

public class Reference {
    public int findI() {
        return 1;
    }
}

public class Example {
    private final Reference r1; // F: +1 & R: +1

    public Example(Reference r1) {
        this.r1 = r1;
    }

    public int m1() {
        return r1.findI();
    }
}

// P = 1/1 = 1
// I = 0

5. Mix

public abstract class Parent {
    protected final int a; // VF: +1

    public Parent(int a) {
        this.a = a;
    }
}

public class Reference {
    public int findI() {
        return 1;
    }
}

public class Example extends Parent {
    public final String f1; // OF: +1

    private final String f2; // OF: +1

    public String getF2() {
        return f2;
    }

    private final String f3; // VF: +1;

    private final Reference r1; // VF: +1; R: +1

    public Example(String f1, String f2, String f3, int a, Reference r1) {
        super(a);
        this.f1 = f1;
        this.f2 = f2;
        this.f3 = f3;
        this.r1 = r1;
    }

    public int m1() { // M: +1
        return l() + a + r1.findI(); // U: +3 (f3, a, r1)
    }

    private int l() {
        return f3.length();
    }
}

// C (Example) = 3/((1 + 2)*(3 + 2)) = 0.2
// I (Example) = 1 - 1/(2 + 3) = 0.8

No comments:

Post a Comment