Code Refactoring

Introduction

Software developers strive to write functionally working code by self-analysis and brainstorming with peers to cover all possible use-cases which may occur in real world (post the deployment of the application).

In Enterprise Application development there are many methodologies & processes to ensure that the application works as per the Requirements & Functional Specifications. Assuming, functional specifications are as per User requirements.

Enterprise Applications are long living creatures expected to survive years and sometimes decades. They are expected to go through cycles of evolution using bug-fixes, enhancements, integration with other applications, removal of obsolete functionalities.

Therefore, it is important to develop a code that is:

  1. Maintainable
  2. Robust
  3. Scalable

At times, these aspects of the code are not obvious but can be achieved by asking the right questions.

Example Application (Step 1)

Let’s start with a functionally working piece of code and improve it step by step.

Please refer to the two classes Person and PersonManager:

public class Person {


  private String firstName;
  private String lastName;
  private String gender;

  public Person(String firstName, String lastName, String gender) {
	super();
	this.firstName = firstName;
	this.lastName = lastName;
	this.gender = gender;
  }

  /*
   * Getters and Setters
   */

}

public class PersonManager {

  public static void main(String[] args) {

	List<Person> people = new ArrayList<>();

	Person p1 = new Person("Alan", "Turing", "Male");
	people.add(p1);

	Person p2 = new Person("Joan", "Clarke", "Female");
	people.add(p2);

	Person p3 = new Person("James", "Gosling", "Male");
	people.add(p3);

	for(Person p : people) {
	  System.out.print(getValue(p.getGender()));
	  System.out.println(" " + p.getFirstName() + " " + p.getLastName());
	}

  }

  private static String getValue(String input) {

	if(input.equalsIgnoreCase("Male")) {
	  return "Mr";
	} else {
	  return "Ms";
	}

  }

}

Although an application which doesn’t take any input from Users or External sources like a DB is not very useful, but let’s stick to it for the sake of simplicity and being to the point on writing the clean code.

For complete code of the initial application, please refer https://gitlab.com/saurabhjain1537/refactoring_workshop/-/tree/master/workshop/src/main/java/com/citra/workshop/person/step1

Maintainability (Step 2)

Let’s think about few questions.

  1. Is this code easy to understand for someone else ?
  2. If I come back to this code after 6 months, can I recall what I did with a quick glance ?

Although these questions seem easy but practically it takes a bit of experience to answer them honestly. Initially it’s worth taking help from peers & colleagues and get honest feedback from some one trustworthy.

Another way is to self-review a functionality which was developed a few months ago.

Let’s analyze this method:

  private static String getValue(String input) {

	if(input.equalsIgnoreCase("Male")) {
	  return "Mr";
	} else {
	  return "Ms";
	}

  }

This method is taking Gender as input and returning a salutation based on the value. This method will be easier to understand if we rename it to getSalutation and rename the method argument to gender. Something like:

  private static String getSalutation(String gender) {

	String salutation = null;
	if(gender.equalsIgnoreCase("Male")) {
	  salutation = "Mr";
	} else {
	  salutation = "Ms";
	}

	return salutation;

  }

Analyze this loop:

	for(Person p : people) {
	  System.out.print(getValue(p.getGender()));
	  System.out.println(" " + p.getFirstName() + " " + p.getLastName());
	}

This loop is printing Full Name with salutation, it makes sense to create a method getFullName and invoke it from the loop:

	for(Person p : people) {
	  System.out.println(getFullName(p));
	}

  }

  private static String getFullName(Person p) {
	return getSalutation(p.getGender()) + " " + p.getFirstName() + " " + p.getLastName();
  }

The complete PersonManager class after refactoring (from Step 1 to Step 2) to make the code more readable, understandable and maintainable:

public class PersonManager {

  public static void main(String[] args) {

	List<Person> people = new ArrayList<>();

	Person alan = new Person("Alan", "Turing", "Male");
	people.add(alan);

	Person joan = new Person("Joan", "Clarke", "Female");
	people.add(joan);

	Person james = new Person("James", "Gosling", "Male");
	people.add(james);

	for(Person p : people) {
	  System.out.println(getFullName(p));
	}

  }

  private static String getFullName(Person p) {
	return getSalutation(p.getGender()) + " " + p.getFirstName() + " " + p.getLastName();
  }

  private static String getSalutation(String gender) {

	String salutation = null;
	if(gender.equalsIgnoreCase("Male")) {
	  salutation = "Mr";
	} else {
	  salutation = "Ms";
	}

	return salutation;
  }

}

For complete code after refactoring for Maintainability, please refer https://gitlab.com/saurabhjain1537/refactoring_workshop/-/tree/master/workshop/src/main/java/com/citra/workshop/person/step2

Robustness (Step 3)

Enterprise Applications interact with multiple Actors like:

  1. Users via Web interface or Notifications
  2. Storage systems like DB or File system
  3. Other applications via messaging queues

A robust application is the one which handles errors due to User input, failure in other systems and report back to the Actors meaningfully.

Few questions to improve robustness:

  1. Does the application validates every input from Actors ?
  2. For Input and Internal processing issues, does it responds with meaningful messages ?
  3. Can anyone easily identify the Module & Functionality causing an issue by analyzing the logs ?

Let’s analyze this method:

  private static String getSalutation(String gender) {

	String salutation = null;
	if(gender.equalsIgnoreCase("Male")) {
	  salutation = "Mr";
	} else {
	  salutation = "Ms";
	}
	return salutation;
  }

It returns the salutation “Ms” for every gender string that is not “Male”. Even in case of misspelled gender or garbage input like “xyz”, it assumes non-Male and return the salutation “Ms” .

It will make more sense if it validates the Gender to be either “Male” or “Female”. There can be a Gender enum containing valid values and a custom exception class PersonValidationException to specify error scenario:

public enum Gender {

  Male,
  Female;
}

public class PersonValidationException extends RuntimeException{

  public PersonValidationException(String arg0, Throwable arg1) {
	super(arg0, arg1);
  }

  public PersonValidationException(String arg0) {
	super(arg0);
  }

}

Now getSalutation method can be refactored as:

  private static String getSalutation(Gender gender) {

	String salutation = null;
	if(Gender.Male == gender) {
	  salutation = "Mr";
	} else if(Gender.Female == gender) {
	  salutation = "Ms";
	} else {
	  throw new PersonValidationException("Unsupported Gender " + gender);
	}

	return salutation;

  }

Person class can be refactored to validate the mandatory fields and dis-allow changes in the member variables values by declaring them as final:

public class Person {

  private final String firstName;
  private final String lastName;
  private final Gender gender;

  public Person(String firstName, String lastName, Gender gender) {

	if(firstName == null || lastName == null || gender == null) {
	  throw new IllegalArgumentException("Mandatory Parameter missing.");

	}
	this.firstName = firstName;
	this.lastName = lastName;
	this.gender = gender;
  }

  /*
   * Getters
   */
}

For complete code after refactoring for Robustness, please refer https://gitlab.com/saurabhjain1537/refactoring_workshop/-/tree/master/workshop/src/main/java/com/citra/workshop/person/step3

Scalability (Step 4)

Enterprise Applications evolve very fast with lots of enhancements. Developers cannot foresee what is going to come-up in future. Still, they can design the application in ways that (a) makes adding of new features & functionalities simple and (b) decreases the impact on already-working features.

There are lots of techniques that can help in designing scalable software like SOLID principles and Design Patterns. Each one of those deserve a separate article, so to keep things simpler in this article we can make just one improvement.

Let’s analyze the following two methods in PersonManager class:

  private static String getFullName(Person p) {
	return getSalutation(p.getGender()) + " " + p.getFirstName() + " " + p.getLastName();
  }

  private static String getSalutation(Gender gender) {

	String salutation = null;
	if(Gender.Male == gender) {
	  salutation = "Mr";
	} else if(Gender.Female == gender) {
	  salutation = "Ms";
	} else {
	  throw new PersonValidationException("Unsupported Gender " + gender);
	}

	return salutation;

  }

These methods have improved a lot and they seem good now. Still, few questions to consider:

  1. Should these methods be part of PersonManager class?
  2. What if some other class using Person class needs to represent the Person data in similar manner? Will it make sense to duplicate the code ?
  3. Is getFullName() performing any business function in PersonManager? Isn’t it just another way Person data is represented?

To allow re-usability of code and to keep only relevant code in every class, these methods can be moved from PersonManager to Person class:

public class Person {


  private final String firstName;
  private final String lastName;
  private final Gender gender;

  public Person(String firstName, String lastName, Gender gender) {

	if(firstName == null || lastName == null || gender == null) {
	  throw new IllegalArgumentException("Mandatory Paramater missing.");

	}

	this.firstName = firstName;
	this.lastName = lastName;
	this.gender = gender;
  }

  /*
   * Getters
   */
  public String getSalutation() {

	String salutation = null;
	if(Gender.Male == gender) {
	  salutation = "Mr";
	} else if(Gender.Female == gender) {
	  salutation = "Ms";
	} else {
	  throw new PersonValidationException("Unsupported Gender " + gender);
	}

	return salutation;
  }

  public String getFullName( ) {

	return getSalutation() + " " + getFirstName() + " " + getLastName();
  }
}

For complete code after refactoring for Scalability, please refer https://gitlab.com/saurabhjain1537/refactoring_workshop/-/tree/master/workshop/src/main/java/com/citra/workshop/person/step4

Conclusion

In this Example application, we started with a piece of code which was functionally working and was providing valid & expected output. Step by step, we improved the quality of code while keeping the same functionality working all the time.

While refactoring an existing code, it is a prerequisite to understand the functionality completely and write comprehensive automated test cases. Execute the automated tests at each step to be sure of retaining the functionality while making the implementation future ready.

Inner Classes in Java

An Inner class is the one which is defined inside another class. This technique doesn’t seem appealing in the beginning but becomes interesting once we start diving deep to explore features like

  1. Connection of inner class with outer class
  2. Multiple inheritance
  3. Anonymous class

Connection with outer class

Inner class can access fields and methods of outer class as if they are its own.

inner-class

In above example, inner class ‘Engine’ can invoke private fields & methods of outer class ‘Car’ like it would access its own members. This is possible because inner class keeps a reference of outer class object which is used to instantiate the inner class object. Like any other class keyword new is used to create object of inner class but there is a difference here – for inner class we need to specify object of outer class dot new keyword, e.g. c.new in this example.

Multiple inheritance

Inner class is second technique to achieve multiple inheritance in Java, first one being interfaces. There are real life problems where logical solution lies in a single class inheriting from multiple classes or abstract classes; this can be achieved by outer class extending one class and multiple inner classes extending different classes or multiple inner classes extending same base class differently. For example,

inner-class-multiple-inheritance

Anonymous inner class

It is a technique using which an object can be created of a class definition by skipping class name. It can be useful in cases where we need only one object of a class.

inner-class-anonymous

In above example, getFuel() method of RacingCar returns an anonymous class object by implementing Fuel interface. Also, note the semi-colon to end the return statement.

Interfaces in Java

Interface in Java is a concept to separate contract (what) from implementation (how).

The keyword interface creates an entity which defines how a class is going to look like with method names, their list & type of arguments and their return type. Interface does not define any method body.

One or more classes can implement the interface to provide the actual method body for all the methods declared in the interface . Since interfaces do not contain any method body, Java does not allow creation of objects of interface. Objects can be created for class that implements the interface.

interface

Similar to inheritance, when defining class for interface we specify name of the interface it is implementing. Here we use the keyword implements to define the class.

A class can implement multiple interfaces by separating Interface names using comma after the implements keyword. An object of such class can be upcasted to any of the interfaces. This is Java’s way of multiple inheritance which is directly not allowed with extends keyword.

multiple-inheritance

Abstract Class

We have discussed about classes and interfaces, there is another entity in Java called Abstract Class which falls into same category but is midway between class and interface. Mid-way because we can provide definition of some methods (like classes) and leave some as abstract or as declaration  (like interfaces). Java does not allow instantiation of abstract class but allows it to be extended like any other class using extends keyword.

abstract-class

In this example, we have created a class TennisPlayer by inheriting from abstract class Person and implementing interface Player. TennisPlayer gets eat() capability from Person but is forced to define work() capability as mandated in same Person abstract class. Similarly, TennisPlayer is forced to implement play() capability of Player interface. Since playing tennis is the work for a tennis player, we reused play as work.

Benefits of using Interfaces & Abstract classes-

  • Re-usability: Using inheritance we can design in a way such that code is reused to its maximum. In above example – common activity of a Human like eat is defined in the abstract class.
  • Extensibility – Using interfaces we can design applications that are easily extensible in future. In above example – SoccerPlayer can be added easily by implementing Player and extending Person.

Word of caution: Using Interfaces is somewhat tricky because it makes sense to use them only when we have multiple implementations either currently or in future. If there is no scope of multiple implementation in future then its wiser to avoid interface and use a class directly.

OOP Fundamentals (3 of 3)

Polymorphism is the third essential feature of OOP.

Polymorphism means many forms – an object can behave in different forms and the correct behavior will be evaluated during run-time only.

Upcasting

Taking the concept of inheritance further,  an object of derived class can be used as its own type or as a type of its base class. Although this statement might appear as incorrect – why would Java allow one type to behave like another type, but if we think in a object oriented manner it will make sense.

Inheritance is making a base class (generic) into a more specific derived class. So, derived class object still posses all the generic behavior of base class. By doing upcasting we are loosing specifics of a derived class but retaining generics of base class.

Let’s take this example

upcast

Output :

Car:Move

Although this is the desired output, but looking at the method park() it doesn’t make sense. In the park() method we are invoking move() of Vehicle but actually move() of Car is invoked.

Dynamic Binding

Connecting a method call to method body is called binding. When this binding happens during compile time, its called static binding. When this binding happens during run time (based on type of object), its called dynamic binding or runtime binding.

In the above example, even when Car object is upcasted to Vehicle in park(), it still knows its type information and behave in polymorphic manner to produce desired output.

Downcasting is opposite of upcasting. Here we cast a generic object into more specific one. While Upcasting is always safe and happens implicitly, Downcasting requires explicit type casting operator and throws a runtime ClassCastException if the instance to be downcasted does not belong to the correct subclass.

downcast

 

 

OOP Fundamentals (2 of 3)

Object Oriented Programming Fundamentals

An important aspect of OOP is reuse of classes using composition and inheritance.

Composition is using objects of existing classes into a new class. It represents ‘has-a‘ relationship. e.g. car has-a engine.

composition
Composition (has-a)

Inheritance is creating new classes as type of existing classes. It represents ‘is-a‘ relationship. In Java code, after specifying the name of derived (new) class use the keyword extends followed by name of base (existing) class .

With inheritance derived class gets all the public & protected attributes and methods of base class. There are two more access specifier (other than private and public) –

  • Protected – Like private, protected  entity is accessible to only the Class which defines it. Unlike private, protected entity of base class is available in the derived class.
  • Package – This is the default access where entity is accessible within the package but is private out of package.
inheritance
Inheritance (is-a)

The derived class may add more attribute & methods to the base class. Internally derived class has a object of base class but externally it exposes an extended interface of base class. So,  derived class is a wrapper over base class with some more specific functionality.

Initialization

Since base class is wrapped inside derived class, it becomes responsibility of derived class to do initialization of base class. Java helps here by calling the constructor of base class from the constructor of derived class, but this automatic help is available only in case of default constructors.

When dealing with constructors having arguments, the derived-class constructor needs to explicitly invoke the base-class constructor using super keyword. For example:

Class Furniture {

  Furniture(int i) {

    print(“Creating Furniture”);

  }

}

Class Table extends Furniture {

  Table(int i) {

    super (i);

    print(“Creating Table”);

  }

}

In the above example, constructor Table(int) invokes super(int) which is effectively Furniture(int). A derived class constructor must initialize the base class before doing anything else because initialization of derived may depend on the base class.

OOP Fundamentals (1 of 3)

Object Oriented Programming Fundamentals

OOP is a paradigm to solve real-world business problems by dividing the problem into a set of objects communicating with each other.

An Object consists of attributes and actions to be performed on the attributes. In programming jargon, attributes are called data members and actions are called member functions. Member functions are also referred as methods in Java.

In Java, Class is a type of objects which have same attributes & methods. In other words, a class is definition of attributes and methods. Once we have a class definition or blueprint ready, we can realize the class to create Objects. This realization is called instantiation in programming jargon.

Although Classes are blueprint and Objects are the instances of class, but the term ‘Object‘ is generally used to represent either of them.

Here’s an example of Account class and two of its instances having id 1010 and 1011.

Account class

Encapsulation and Abstraction

A class encapsulates data members and member functions into a single reusable entity. Re-usability of classes a benefit of using OOP; a class once written & tested can be re-used in different scenarios to solve similar business problems.

Abstraction is hiding internal details while exposing only relevant & required information to outside world. In java, abstraction is achieved by use of access specifiers. Here we are introducing only two of four access specifiers in Java –

Public – The entity (class, data member, method) is accessible to everyone.

Private – The entity is accessible to only the Class which defines it.

Let’s take real world example of a Car – which is made-up of a lot of attributes like engine, steering, brake pedal, gear-box, tyres, brake shoe, axle, etc. Car exposes interface to the user (methods) like changing gear, applying brake, etc. without telling the user that there is a brake shoe. Someone driving a car does not need to know that it has brake shoe but they need to know how to apply brake.

The benefit to using abstraction is that Car manufacturer can actually switch a mechanical brake with a hydraulic brake without any impact on the way people use or apply brake.

A software world example can be a AlarmLogger class – whose job is to write alarms in a file using a specific format. This class exposes a public method logAlarm and hides internal file-handing and formatting from clients calling this class by keeping them as private. This same class can be reused in multiple Programs like ecommerce app, banking app.