Archives May 2023

Refactoring code to add lambda laziness 2 – Functional style programming – extending API

Fixing in functional fashion

How about providing this fix in a functional programming fashion? Practically, all we want is to lazy download the applicatons’s dependenies. Since laziness is the functional programming spciality, and we’ve just got familiar with the Supplier (see the previous problem) we can start as follows:

public class ApplicationDependency {
        
  private final Supplier<String> dependencies
    = this::downloadDependencies;  
  …
  public String getDependencies() {
    return dependencies.get();
  } 
  …
  private String downloadDependencies() {
         
    return “list of dependencies downloaded from repository ”    
     + Math.random();
  }  
}

First, we defined a Supplier that calls the downloadDependencies() method. We know that the Supplier is lazy so nothing happens until its get() method is explicitly called.Second, we have modified getDependencies() to return dependencies.get(). So, we delay the application’s dependencies downloading until they are explicitly required.Third, we modified the return type of the downloadDependencies() method from void to String. This is needed for the Supplier.get().This is a nice fix but it has a serious shortcoming. We lost the caching! Now, the dependencies will be downloaded at every getDependencies() call.We can avoid this issue via memoization (https://en.wikipedia.org/wiki/Memoization). We have covered this concept in Chapter 8 of The Complete Coding Interview Guide in Java. However, in a nutshell, memoization is a technique used to avoid duplicate work by caching results that can be reused later.Memoization is a technique commonly applied in Dynamic Programming, but there are no restrictions or limitations. For instance, we can apply it in functional programming. In our particular case, we start by defining a functional interface that extends the Supplier interface:

@FunctionalInterface
public interface FSupplier<R> extends Supplier<R> {}

Next, we provide an implementation of FSupplier that basically cashes the unseen results and serve from the cache the already seen ones:

public class Memoize {
  private final static Object UNDEFINED = new Object();
  public static <T> FSupplier<T> supplier(
    final Supplier<T> supplier) {
    AtomicReference cache = new AtomicReference<>(UNDEFINED);     
    return () -> {                      
          
      Object value = cache.get();          
          
      if (value == UNDEFINED) {              
              
        synchronized (cache) {
                 
          if (cache.get() == UNDEFINED) {
                      
            System.out.println(“Caching: ” +  supplier.get());
            value = supplier.get();
            cache.set(value);
          }
        }
      }
          
      return (T) value;
    };
  }
}

Finally, we replace our initial Supplier with FSupplier as follows:

private final Supplier<String> dependencies
  = Memoize.supplier(this::downloadDependencies);

Done! Our functional approach takes advantage of Supplier’s laziness and can cache the results.

Refactoring code to add lambda laziness – Functional style programming – extending API

182. Refactoring code to add lambda laziness

In this problem let’s have a refactoring session meant to transform a dysfunctional code into a functional one. We start from the following given code – a simple class mapping information about applications dependencies:

public class ApplicationDependency {
  
  private final long id;
  private final String name;
  private String dependencies;
  public ApplicationDependency(long id, String name) {
    this.id = id;
    this.name = name;
  }
  public long getId() {
    return id;
  }
  public String getName() {
    return name;
  } 
  
  public String getDependencies() {
    return dependencies;
  }
  
  private void downloadDependencies() {
        
    dependencies = “list of dependencies
      downloaded from repository ” + Math.random();
  }  
}

Why did we highlight the getDependencies() method? Because this is the point in the application having a dysfunctionality. More precisely, the following class needs the dependencies of an application in order to process them accordingly:

public class DependencyManager {
  
  private Map<Long,String> apps = new HashMap<>();
  
  public void processDependencies(ApplicationDependency appd){
      
    System.out.println();
    System.out.println(“Processing app: ” + appd.getName());
    System.out.println(“Dependencies: “
      + appd.getDependencies());
      
    apps.put(appd.getId(),appd.getDependencies());       
  }  
}

This class relies on the ApplicationDependency#getDependecies() method which just returns null (the default value of the dependencies fields). The expected application’s dependencies were not downloaded since the downloadDependecies() method was not called. Most probably, a code reviewer will signal this issue and raise a ticket to fix it.

Fixing in imperative fashion

A possible fix will be as follows (in ApplicationDependency):

public class ApplicationDependency {
  
  private String dependencies = downloadDependencies();
  …
  public String getDependencies() {
           
    return dependencies;
  }
  …
  private String downloadDependencies() {
         
    return “list of dependencies downloaded from repository “
      + Math.random();
  }
}

Calling downloadDependencies() at dependencies initialization will definitely fix the problem of loading the dependencies. When the DependencyManager will call getDependencies() it will have access to the downloaded dependencies. But, is this a good approach? I mean downloading the dependencies is a costly operation and we do it every time an ApplicationDependency instance is created. If the getDependencies() method is never called then this costly operation doesn’t pay off the effort.So, a better approach will be to postpone the download of the application’s dependencies until getDependencies() is actually called:

public class ApplicationDependency {
  private String dependencies;
  …
  public String getDependencies() {
             
    downloadDependencies();      
     
    return dependencies;
  }
  …
  private void downloadDependencies() {
         
    dependencies = “list of dependencies
      downloaded from repository ” + Math.random();
  }  
}

This is better but is not the best! This time, the application’s dependencies are downloaded every time the getDependencies() method is called. Fortunately, there is a quick fix for this. We just need to add a null check before performing the download:

public String getDependencies() {
      
  if (dependencies == null) {
    downloadDependencies();
  }
       
  return dependencies;
}

Done! Now, the application’s dependencies are downloaded only at the first call of the getDependencies() method. This imperative solution works like a charm and will pass the code review.