Coding Bootcamp: Writing Maintainable Code

Who Am I?

Which one is easy to work with?

Evolution of Junit

Lehman's laws of evolution

Code Quality: Why to care?

Part 1 - Software Metrics

Metrics - What and Why?


Object Oriented Metrics

OO Metrics (Size)

OO Metrics (Size - Hierarchy)

OO Metrics (Cohesion and Coupling)

Consider a class C with n methods M1, M2..., Mn.

Let {Ij} = set of instance variables used by method Mj. There are n such sets I1 ,…, In

P = {(Ii, Ij) | (Ii ∩ Ij ) = ∅}

Q = {(Ii, Ij) | (Ii ∩ Ij ) ≠ ∅}

LCOM = (||P| - |Q||)/|P|, if |P| > 0

= 0, otherwise

OO Metrics (Cohesion and Coupling)

High LCOM indicate the violation of the Single Responsibility Principle.

OO Metrics (Cohesion and Coupling)

OO Metrics (Complexity)

OO Metrics (Complexity)

Tools to compute metrics

Part 2 - Software Smells

Jugaad

Jugaad

Jugaad

Jugaad

Jugaad

Jugaad

Jugaad

Smells are Jugaads in software

Smell Granularity

Implmentation smells

Design Smells

Example

Broken Modularization

This smell arises when members of an abstraction are broken and spread across multiple abstractions (when ideally they should have been localized into a single abstraction).

Example

Insufficient Modularization

This smell arises when an abstraction exists that has not been completely decomposed and a further decomposition could reduce its size, implementation complexity, or both.

Example

Broken Hierarchy

This smell arises when a supertype and its subtype conceptually do not share an “IS-A” relationship resulting in broken substitutability.

Example

public class Throwable {
  // following method is available      
  // from Java 1.0 version. 
  // Prints the stack trace as a string 
  // to standard output 
  // for processing a stack trace, 
  // we need to write 
  // regular expressions 
  public void printStackTrace();     

// other methods elided 
}

Missing Abstraction

This smell arises when clumps of data or encoded strings are used instead of creating a class or an interface.

A tool to detect these smells


http://www.designite-tools.com

Further reading

Part 3 - Refactoring

Once upon a time...

Definition

Why to Refactor?

Better code/design quality leads to improved productivity as well as high morale and motivation of the team

Refactoring Techniques

Are these names conveying the right message??

double round_type5(double n)
{
    double d,d1,d2,d3;
    
    d=n*100;
    d=(int)d/100;

    d1= ((d*100) - ((int)d) * 100)/100; 
    d1+=2.5;
    d2= (((int)(d1/5))*5)/100;
    d3 = (int)d +d2;
    return d3;
}

What about this?

double roundToNearestPoint05(double number_in)
{
    double roundedNo, fractionalPart, roundedFracPart, result;

    roundedNo=number_in*100;
    roundedNo=(int)roundedNo/100;

    fractionalPart= ((roundedNo*100) - ((int)roundedNo) * 100)/100; 
    fractionalPart+=2.5;
    roundedFracPart= (((int)(fractionalPart/5))*5)/100;
    result = (int)roundedNo +roundedFracPart;
    return result;
}
double round_type5(double n)
{
    double d,d1,d2,d3;
    
    d=n*100;
    d=(int)d/100;

    d1= ((d*100) - ((int)d) * 100)/100; 
    d1+=2.5;
    d2= (((int)(d1/5))*5)/100;
    d3 = (int)d +d2;
    return d3;
}

Refactoring - “Renaming variable to more meaningful name”

Example

void printAppObjects(cInvoice *invoiceObj, list<cItem *> itemContainer)
{
    list<cItem*>::iterator itemItr = itemContainer.begin();
    
    while(itemItr != itemContainer.end())
    {
        cout<<(*itemItr)->getItemName()<<endl;
        cout<<(*itemItr)->getItemPrice()<<endl;
        cout<<(*itemItr)->getItemTaxes()<<endl;
    }
    cout<<"Total items:"<<invoiceObj.getItemCount()<<endl;
    cout<<"Total amount:"<<invoiceObj.getTotalAmount()<<endl;
    cout<<"Total taxes:"<<invoiceObj.getTotalTaxes()<<endl;
}

A better way

void printAppObjects(cInvoice *invoiceObj, list<cItem *> itemContainer){
    printAllItems(itemContainer);
    printInvoiceObj(invoiceObj); 
}
void printAllItems(list<cItem*> itemContainer) {    
    list<cItem*>::iterator itemItr = itemContainer.begin();
    while(itemItr != itemContainer.end()) {
        cout<<(*itemItr)->getItemName()<<endl;
        cout<<(*itemItr)->getItemPrice()<<endl;
                cout<<(*itemItr)->getItemTaxes()<<endl;     
    }
 }
void printInvoiceObj(cInvoice *invoiceObj){
    cout<<"Total items:"<<invoiceObj.getItemCount()<<endl;
    cout<<"Total amount:"<<invoiceObj.getTotalAmount()<<endl;
    cout<<"Total taxes:"<<invoiceObj.getTotalTaxes()<<endl; 
}

Refactoring - “Extract method”

Example

if(itemObj.getAvailableUnits() > requiredUnits && 
  itemObj.getExpiryDate()>date() && 
  itemObj.getUnitWeight()== requiredUnitWeight)
{
        …
}
else
{
        …
}

A better way

if(isItemSellable(…))
{
        …
} 
else 
{
        …
}

int isItemSellable(…) {
    return (itemObj.getAvailableUnits() > requiredUnits 
    && itemObj.getExpiryDate()>date() && 
    itemObj.getUnitWeight()== requiredUnitWeight);
}

Refactoring - “Decompose conditional”

Example

switch(angle)
{
  case 90:
    rotateBy90(…);  
    break;
  case 180:
    rotateBy180(…);     
    break;
}

int rotateBy90(…)
{ …}

int rotateBy180(…)
{… }

Refactoring - "Parameterized Method"

rotate(angle);

int rotate(angle, …)
{ …}

Approching "design-level" refacorings

Example

class GraphicsDevice {
 public void setFullScreenWindow(Window w) {
        if (w != null) {
            if (w.getShape() != null) { w.setShape(null); }
            if (w.getOpacity() < 1.0f) { w.setOpacity(1.0f); }
            if (!w.isOpaque()) {
                Color bgColor = w.getBackground();
                bgColor = new Color(bgColor.getRed(), bgColor.getGreen(), bgColor.getBlue(), 255);
                w.setBackground(bgColor);
            }
        }
…
}

Refactoring - “Move Method”

Refactoring - “Move Method”

Example

Refactoring - "Extract Class"

Refactoring - "Extract Class"

Other Refactorings

Using IDEs to refactor

Further reading


Creative Commons Licence
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.