X

Break New Ground

Refactoring using Functional Programming

Arvind Kumar GS
Senior Member of Technical Staff

Refactoring

Refactoring is used for making code more readable and understandable to humans, thereby maintainable. It does not effect the functionality. In fact it should not effect the functionality.

When

You should consider refactoring when you find it difficult to make changes to your code. There are a few concepts that help in identifying when you need to refactor, like Code Smells. Code is said to smell, when:
  1. An object refers to the internal attributes of another object (code envy)
  2. Method is too big
  3. Too many if-else conditions
  4. Duplication of code etc

How

Common patterns occur in software engineering. These patterns have been identified and a set of best practices has been defined to model these patterns. These set of best practices are called Design patterns. Design patterns makes your code:

  • Extensible
  • Loosely coupled
  • Readable
  • Reuseable (as an API)

There are multiple design patterns that you can look up here.

Application

Let us look at the following example. Suppose I need to write a program as follows:

Name: ‘extract-properties’.

Description: Extracts and prints value of properties from file

Input:

  • file (of type — xml | property | json ..) and
  • property to extract
  • dynamic map (replaces key (if) found in the property values, with corresponding map values.)

Now we can split the problem into smaller problems as:

  1. Extract properties from given file (Extract method should support file types like — xml, property, json.)
  2. Replace placeholders with dynamic values

There are three ways to solve the problem.

  1. Procedural
  2. Object Oriented
  3. Functional

Lets evaluate all three.

Procedural

Procedural way of solving the problem,

  • main method reads file content and calls extract method.
  • extract method parses the content based on type, using an if condition that calls the specific parse method and extracts the properties and returns these properties.
  • main method now call replace method to replace the placeholders with its dynamic values.

Now suppose you need to add another handler, say ‘YAML’ file type. You will need to :

  • Add another if else block in ‘extract’ method.

Advantages of the above approach:

  1. Ease to write when extensions/modification are limited.

What are the disadvantages of the above approach?

  1. This causes a bottleneck on the extract method. In a team of developers, constant updates to a single entity by all the developers will result in merge conflicts.
  2. The extract method will increase in size as extensions are added. This will make it less readable.
  3. Having business logic interspersed with your I/O operations will result in unit test cases that will need actual resource versus a mock.
  4. The resource I/O operations code cannot be reused in other parts of the code as it is tightly coupled to the business logic.

Object Oriented

Let’s have a base class ResourceHandler. This defines methods extract () and replace (). extract() needs to allow for custom implementation to support future enhancements. While replace() needs to be common logic to be used irrespective of the custom extract implementation.

We can model this as follows:

As you see ResourceHandler has methods:

1. public Map handle(). This contains the business logic, which for our purposes can be as simple as:

public Map handle() {
  extract();
  replace();
}

2. protected abstract void extract()

3. private void replace()

Now the abstract method ‘extract’ needs to be overridden by its child classes:

  1. XmlResource
  2. JsonResource
  3. PropertiesResource

Below is the main class.

Advantages of this approach:

  1. This structure allows to add new extensions without modifying the base ‘handler’ method, that is invoked to extract and replace properties. So child classes cannot corrupt the business logic.
  2. There is no more a bottleneck on the extract method as it was with the procedural code. Now each custom extract method has it’s own implementation in it’s own class. This will reduce the possible merge-conflicts between multiple developers working on different custom extract implementation.
  3. Object ‘ResourceHandler’ is closed for modification while being open for extension. This is one of the pillars of the SOLID principle. For example if, ResourceHandler is shared as jar, it can still be extended to support additional resource types, but the underlying base logic is still restricted.

Disadvantages:

  1. The IO logic to extract properties for given type, is still coupled to the business logic, due to the structure of the Base class. As the only public method is handle, which calls the extract method and replace method. So we cannot use extract in isolation.

Functional

Lets implement functional code using interfaces as follows:

ResourceHandler interface defines a single method ‘extract’ that takes the file and list of properties to extract, and returns the corresponding extracted properties from the input file.

We will utilize the ResourceHandler in the business logic, and inject the specific implementation in to the class ExtractReplace that encapsulates the business logic as follows:

class ExtractReplace {
  private Map extracted_properties;

  public ExtractReplace(File file, List properties,Map dynamic, ResourceHandler handler) {
    Map props = handler.extract(file, properties);
    extracted_properties = replace(props, dynamic);
  }

  private Map replace(Map properties, Map dynamic) {...}
  @Override
  public String toString() {
    return extracted_properties.toString();
  }
}

Now the main class will look like this:

The main class creates an object of type ExtractReplace by passing an implementation of ResourceHandler Interface via the Lambda construct as seen. This demonstrates the capability to define adhoc anonymous implementations which do not need to be housed in a type of it’s own, thereby reducing structural complexity.

We can also follow a more structured approach, where the different flavours of ResourceHandlers are housed in their own types.

This structure allows to use XmlHandler/JsonHandler … objects independently for extracting properties. This is an advantage over Object-oriented approach shown above.

The main class can create objects of specific types of ResourceHandler and pass them to the ExtractReplace class as follows:

You can further decouple the if-else code for instantiating ResourceHandler by having a builder class (Using Builder Pattern).

The enhanced ExtractReplace class with ‘builder’ pattern will look like this:
class ExtractReplace{
  private Map extracted_properties;

  static class Builder {
    private File file;
    private List properties;
    private Map dynamic;
    private ResourceHandler handler;
    static Builder getInstance(File file, List properties, Map dynamic){
      this.file = file;
      this.properties = properties;
      this.dynamic = dynamic;
      if (file.getName().endsWith(".json"))
        this.handler = new JsonHandler();
      else if(file.getName().endsWith(".xml"))
        this.handler = new XmlHandler();
    }
    public ExtractReplace build() {
      new ExtractReplace(this);
    }
  }
  private ExtractReplace(Builder builder) {
    Map props = builder.handler.extract(builder.file, builder.properties);
  }
  private Map replace(Map properties, Map dynamic) { ... }

  @Override
  public String toString(){
    return extracted_properties.toString();
  }
}

More on the builder pattern here.

Advantages:
  1. Over and above the advantages that are provided by Object Oriented approach, this also separates the extraction logic from the business logic. Hence you can reuse this extract method.
  2. You can follow a structured approach by creating different implementation classes of ResourceHandler, which can be resused.
  3. You can also follow a adhoc anonymous approach using Lambda functions. This is useful if you are not reusing the extraction logic else where and want to reduce the structural complexity.

Final Notes

The above functional code is an implementation of Strategy pattern, similar to Collections.sort, where you can pass a custom Comparator
implementation. As you can see the code is extensible, easier to
consume, understand and extend.

 

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.