Using Scala functions to reduce duplication, as alternative to Strategy and Template method design patterns

It can be more convenient to use a function/method as a parameter than having to create an interface with only one method.

Scala supports functions/methods as parameters while Java requires an interface and implementations.

When you use an object-oriented language and want to reduce some duplication within some methods which only have some small differences, you often try to do it with a GoF pattern such as Strategy or Template Method.

However, when the amount of duplicated code is not very big, it can feel like overkill to try to improve the situation with some refactoring like that, since the needed verbose Java boilerplating code can make the resulting code not looking much better, even though you got rid of the duplication.

In the refactored Java code example below, anonymous classes is instantiated directly within the method invocation (i.e. without creating a variable to store the object) which reduces the duplicated typing.

The reason is that if you would want to first use a variable to store the instance of the "Strategy class" used below, then you would have to (with Java) duplicate the types in both sides of the assignement, e.g. ElementMappingStrategy<String, Integer> strategy = new ElementMappingStrategy<String, Integer>() ... (Such duplication is not needed in Scala, thanks to the type inference mechanism)

Anyway, even without such redundant type declaration, you still need to (with Java) duplicate the method signature many times in every implementation of the class, and the resulting Java code, as you will see below, is quite verbose.

Here is a "preview example" (of the code further down the page) of the verbose code needed for creating a simple little strategy in Java:

	return getMappedList(
	    stringsWithLengthsToBeReturned,
	    new ElementMappingStrategy<String, Integer>() {
		public Integer getMappedValue(final String value) {
		    return value.length();
		}
	    }
	);

The corresponding Scala code (which also is statically typed!) will be written like below:

	getMappedList(
	    stringsWithLengthsToBeReturned, 
	    (value : String) => value.length
	)

Now, let us go on with the example and suppose that you initally have some methods that returns and recevies List parameters as in the interface below.

/**
 * All methods defined in this interface are return a List, and
 * also receives at least one parameter with a List.
 * The actual type of the List elements are different,
 * i.e. some methods both receive and return a List<String>
 * while another method receives a List<String> but returns a List<Integer>,
 * and yet another both receives and returns List<Integer>.  
 */
public interface ListMapper {

    List<String> getUpperCasedStrings(List<String> stringsToBeReturnedUpperCased);

    List<String> getLowerCasedStrings(List<String> stringsToBeReturnedLowerCased);

    List<Integer> getLengthsOfStrings(List<String> stringsWithLengthsToBeReturned);

    List<Integer> getWithEachElementMultiplied(List<Integer> integersToBeMultiplied, int factorToMultiplyEachElementWith);
}

A typical initial implementation of the above interface would problably look something like the code below, with an unpleasant smell of code duplication which you probably would like to refactor away.

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public final class JavaListMapperWithDuplicatedIterationCode implements ListMapper {

    public List<String> getUpperCasedStrings(final List<String> stringsToBeReturnedUpperCased) {
        final List<String> upperCasedStrings = new ArrayList<String>();
        for (String string : stringsToBeReturnedUpperCased) {
            upperCasedStrings.add(string.toUpperCase());
        }
        return Collections.unmodifiableList(upperCasedStrings);
    }

    public List<String> getLowerCasedStrings(final List<String> stringsToBeReturnedLowerCased) {
        final List<String> lowerCasedStrings = new ArrayList<String>();
        for (String string : stringsToBeReturnedLowerCased) {
            lowerCasedStrings.add(string.toLowerCase());
        }
        return Collections.unmodifiableList(lowerCasedStrings);
    }

    public List<Integer> getLengthsOfStrings(final List<String> stringsWithLengthsToBeReturned) {
        final List<Integer> lengthsOfStrings = new ArrayList<Integer>();
        for (String string : stringsWithLengthsToBeReturned) {
            lengthsOfStrings.add(string.length());
        }
        return Collections.unmodifiableList(lengthsOfStrings);
    }

    public List<Integer> getWithEachElementMultiplied(
        final List<Integer> integersToBeMultiplied, 
        final int factorToMultiplyEachElementWith
    ) {
        final List<Integer> products = new ArrayList<Integer>();//product=factor*factor
        for (Integer factor : integersToBeMultiplied) {
            products.add(factor * factorToMultiplyEachElementWith);
        }
        return Collections.unmodifiableList(products);
    }
}

The above code contains a lot of similar code, i.e. each of the method instantiates a List, which in Java has to be typed in two places, for example when you instantiate a List of Integer's you need to specify 'Integer' at both the sides of the assignment/declaration/instantiation row.

It also has very similar iteration code, and similar code that adds to another list to be returned. It would be nice to reuse as much of the code as possible, and only use different code for the things that actually are different.

It is possible to reuse lots of the code, by using Generics, and some Design pattern, for example using a version of the Strategy pattern as below. (though not a 'pure' version of the Strategy pattern, i.e. not exactly as described in the GoF book, since the strategy is used below as a parameter rather than being aggregated as a state with a concrete instance being set with a 'setStrategy' method)

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public final class JavaListMapperImplementationWithStrategies implements ListMapper {

    private interface ElementMappingStrategy {
        U getMappedValue(T value);
    }

    /**
     * This helper method provides the kind of code that was previously duplicated
     * i.e. the code that iterated the input List and creates the List to be returned. 
     * Though, the actual differences for the different methods will be handled in a concrete 
     * instance which implements the interface, and needs to be provided as a parameter. 
     */
    private <T, U> List<U> getMappedList(
        final List<T> elementsToBeMapped,
        final ElementMappingStrategy<T, U> elementMappingStrategy
    ) {
        final List<U> returnedElements = new ArrayList<U>();
        final Iterator<T> iterator = elementsToBeMapped.iterator();
        while(iterator.hasNext()) {
            returnedElements.add(elementMappingStrategy.getMappedValue(iterator.next()));
        }
        return Collections.unmodifiableList(returnedElements);
    }

    // Note that even though this is the refactored version without the previous duplication
    // which now is reused in the method above, there is still lots of verbose code
    // in the methods below, which looks like duplication
		
    public List<String> getUpperCasedStrings(final List<String> stringsToBeReturnedUpperCased) {
        return getMappedList(
            stringsToBeReturnedUpperCased,
            new ElementMappingStrategy<String,String>() {
                public String getMappedValue(final String value) {
                    return value.toUpperCase();
                }
            }
        );
    }

    public List<String> getLowerCasedStrings(final List<String> stringsToBeReturnedLowerCased) {
        return getMappedList(
            stringsToBeReturnedLowerCased,
            new ElementMappingStrategy<String,String>() {
                public String getMappedValue(final String value) {
                    return value.toLowerCase();
                }
            }
        );
    }

    public List<Integer> getLengthsOfStrings(List<String> stringsWithLengthsToBeReturned) {
        return getMappedList(
            stringsWithLengthsToBeReturned,
            new ElementMappingStrategy<String, Integer>() {
                public Integer getMappedValue(final String value) {
                    return value.length();
                }
            }
        );
    }

    public List<Integer> getWithEachElementMultiplied(
        final List<Integer> integersToBeMultiplied,
        final int factorToMultiplyEachElementWith
    ) {
        return getMappedList(
            integersToBeMultiplied,
            new ElementMappingStrategy<Integer, Integer>() {
                public Integer getMappedValue(final Integer value) {
                    return factorToMultiplyEachElementWith * value;
                }
            }
        );
    }

}

As you can see above, we got rid of the duplicated iteration code, and the duplicated code that created and populated the list to be returned.

However, it did require some effort for little benefit, i.e. the resulting code is just as long, and still have a smell of duplication, since when you look at the methods they have similar code for instantianting an object which implements the method in the interface.

With Java, it is difficult to achieve something that looks much better than the code above, but with Scala you can improve the situation a little bit.

Below is a Scala implementation (of the same Java interface, and yes the keyword 'extends' is used for implementing a Java interface, i.e. it is not a typing error if you believed so) which is very similar to the above Java implementation, but does not need to create nor implement an interface, but can instead use a function as below:

// This class contains code similar to the java implementation class 'JavaListMapperImplementationWithStrategies'
// but with less code, and no need to create an interface
final class ScalaListMapperSimilarToJavaImplementationWithStrategies extends ListMapper {
  
  // ListMapper is a Java interface, so therefore Java types are needed in the method signatures

  final def getUpperCasedStrings(stringsToBeReturnedUpperCased: java.util.List[String]): java.util.List[String] = {
    getMappedList(stringsToBeReturnedUpperCased, (s : String) => s.toUpperCase)
  }

  final def getLowerCasedStrings(stringsToBeReturnedLowerCased: java.util.List[String]): java.util.List[String] = {
    getMappedList(stringsToBeReturnedLowerCased, (s : String) => s.toLowerCase)
  }

  final def getLengthsOfStrings(stringsWithLengthsToBeReturned: java.util.List[java.lang.String]): java.util.List[Integer] = {
    getMappedList(stringsWithLengthsToBeReturned, (s : String) => s.length)
  }

  final def getWithEachElementMultiplied(
    integersToBeMultiplied: java.util.List[Integer],
    factorToMultiplyEachElementWith: Int
  ) : java.util.List[Integer] = {
    // If we had been dealing only with Scala Int, i.e. without using any Java Integer,
    // then something like below should have worked:
    // getMappedList(integersToBeMultiplied, (i : Int) => i * factorToMultiplyEachElementWith )
    // but instead some more usages of types for Int and Integer amd intValue method is used below: 
    getMappedList(integersToBeMultiplied, (i : Integer) => (i.intValue * factorToMultiplyEachElementWith): Integer )
  }

  // Please note that with this Scala code, it is not necessary to use any interface such as
  // the previously used Java interface 'ElementMappingStrategy' with the
  // method 'U getMappedValue(T value);'
  // but instead the Scala language supports function parameters, i.e. the previously used java interface method
  // corresponds to the below function 'T => U' , where T is the type of the elements in the input List
  // while U is the type of the elements in the return List,

  // Note: This kind of iteration code as below is normally not used in Scala,
  // but is should indeed be easy to understand for alla java developers
  private def getMappedList[T,U](
    elementsToBeMapped: java.util.List[T],
    elementMappingStrategy: (T => U)
  ): java.util.List[U] = {
    val returnedElements = new java.util.ArrayList[U]
    val iterator = elementsToBeMapped.iterator
    while(iterator.hasNext) {
      returnedElements.add(elementMappingStrategy(iterator.next))
    }
    return returnedElements // does not work here: Collections.unmodifiableList(returnedElements);
  }

}

The above Scala code was written in such a way that it would be very easy for a Java developer (without any current Scala skills) to understand the code, but it should mentioned that "while loops" are generally discouraged in Scala. (see for example chapter 7.2 in the book 'Programming in Scala')

An alternative Scala implementation is shown below, which could have been shorter if the code would not have to interact with Java code, i.e. in this example the Scala class is implementing a Java interface, which for example uses Java Integer, while Scala has a type Int which Java does not know about.

import java.util.Arrays
import scala.collection.jcl.Conversions.convertList
import org.scala_tools.javautils.Imports._ // http://github.com/jorgeortiz85/scala-javautils 
final class ScalaListMapper extends ListMapper {

  // ListMapper is a Java interface, so therefore Java types are needed in the method signatures
  
  final def getUpperCasedStrings(stringsToBeReturnedUpperCased: java.util.List[String]): java.util.List[String] = {
    // Note that the typing below is optional (see next method)
    val sequence: Seq[String] = stringsToBeReturnedUpperCased.map(_.toUpperCase)
    sequence.asJava // this row would not be needed if we would not be interacting with types in Java code
  }

  final def getLowerCasedStrings(stringsToBeReturnedLowerCased: java.util.List[String]): java.util.List[String] = {
    // Below, automatic type inference is used,
    // but please note that static compile time type checking is still used ! 
    val sequence = stringsToBeReturnedLowerCased.map(_.toLowerCase)

    // this would be an example of an alternative implementation for the above 
    //  val sequence = for {
    //    s <- stringsToBeReturnedLowerCased
    //  } yield s.toLowerCase
    
    sequence.asJava // this row would not be needed if we would not be interacting with types in Java code
  }


  final def getLengthsOfStrings(stringsWithLengthsToBeReturned: java.util.List[java.lang.String]): java.util.List[Integer] = {
    val scalaSequence: Seq[Int] = stringsWithLengthsToBeReturned.map(_.length)
    asJava(scalaSequence); // this row would not be needed if we would not be interacting with types in Java code
  }

  final def getWithEachElementMultiplied(
    integersToBeMultiplied: java.util.List[Integer],
    factorToMultiplyEachElementWith: Int
  ) : java.util.List[Integer] = {
    val products = integersToBeMultiplied.map( _.intValue * factorToMultiplyEachElementWith )
    asJava(products) // this row would not be needed if we would not be interacting with types in Java code
  }

  // This method is only used for conversion between Scala and Integer, and would not be needed in Scala
  // code that does not interact with Java code
  private final def asJava(listOfIntegers: Seq[Int]): java.util.List[Integer] = {
    val javaList = new java.util.ArrayList[Integer]
    listOfIntegers.foreach(myInt => javaList.add(myInt) )
    javaList
  }

}

And below we have some JUnit tests that can be executed for all above implementations of the Java interface, including the Scala implementatoins shown above.

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNotNull;
/**
 * Test class that executes the tests for all four implementations above, to verify that behaviour is the same
 * except from verifying that the Scala code is compatible with Java code,
 * i.e. the Scala classes implement a Java interface, and they are also executed from Java code. 
 */
public final class ListMapperTest {

    private static List<ListMapper> listMappers = new ArrayList<ListMapper>();
    
    @BeforeClass
    public static void beforeClass() {
        listMappers.add(new JavaListMapperWithDuplicatedIterationCode());
        listMappers.add(new JavaListMapperImplementationWithStrategies());
        listMappers.add(new ScalaListMapperSimilarToJavaImplementationWithStrategies());
        listMappers.add(new ScalaListMapper());
    }

    @Test
    public void executeAllTestsForAllImplementations() {
        for (ListMapper listMapper : listMappers) {
            verifyLowerCasing(listMapper);
            verifyUpperCasing(listMapper);
            verifyLengthOfStrings(listMapper);
            verifyMultiplications(listMapper);
        }
    }

    private void verifyLowerCasing(final ListMapper listMapper) {
        final List<String> lowerCasedNames = listMapper.getLowerCasedStrings(NAMES);
        assertEquals(NAMES.size(), lowerCasedNames.size());
        assertEquals(NAME_1_TOMAS.toLowerCase(), lowerCasedNames.get(0));
        assertEquals(NAME_2_BO.toLowerCase(), lowerCasedNames.get(1));
        assertEquals(NAME_3_PER.toLowerCase(), lowerCasedNames.get(2));
        assertEquals(NAME_4_KARL.toLowerCase(), lowerCasedNames.get(3));
    }

    private void verifyUpperCasing(final ListMapper listMapper) {
        final List<String> upperCasedNames = listMapper.getUpperCasedStrings(NAMES);
        final String errorMessage = getErrorMessage(listMapper);
        assertNotNull(errorMessage, upperCasedNames);
        assertEquals(errorMessage, NAMES.size(), upperCasedNames.size());
        assertEquals(NAME_1_TOMAS.toUpperCase(), upperCasedNames.get(0));
        assertEquals(NAME_2_BO.toUpperCase(), upperCasedNames.get(1));
        assertEquals(NAME_3_PER.toUpperCase(), upperCasedNames.get(2));
        assertEquals(NAME_4_KARL.toUpperCase(), upperCasedNames.get(3));
    }

    private void verifyLengthOfStrings(final ListMapper listMapper) {
        final List<Integer> lengthsOfStrings = listMapper.getLengthsOfStrings(NAMES);
        final String errorMessage = getErrorMessage(listMapper);
        assertNotNull(errorMessage, lengthsOfStrings);
        assertEquals(errorMessage, NAMES.size(), lengthsOfStrings.size());
        assertEquals(NAME_1_TOMAS.length(), lengthsOfStrings.get(0).intValue());
        assertEquals(NAME_2_BO.length(), lengthsOfStrings.get(1).intValue());
        assertEquals(NAME_3_PER.length(), lengthsOfStrings.get(2).intValue());
        assertEquals(NAME_4_KARL.length(), lengthsOfStrings.get(3).intValue());
    }

    private void verifyMultiplications(ListMapper listMapper) {
        final List<Integer> result = listMapper.getWithEachElementMultiplied(Arrays.asList(3, 5, 7), 3);
        final String errorMessage = getErrorMessage(listMapper);
        assertNotNull(errorMessage, result);
        assertEquals(errorMessage, 3, result.size());
        assertEquals(errorMessage, 9, result.get(0).intValue());
        assertEquals(errorMessage, 15, result.get(1).intValue());
        assertEquals(errorMessage, 21, result.get(2).intValue());
    }

    private String getErrorMessage(ListMapper listMapper) {
        return "The failing implementation class was: " + listMapper.getClass().getName();
    }

    // All names (except the first name) are chosen, and ordered in the list, in such a way that both the length
    // and the index in the list is included
    // in the name of the constant, which is good to keep in mind for easier readability in the assertions,
    // for example, the third name in the list (Per) has three letters and its constant is named with the prefix 'NAME_3'
    private final static String NAME_1_TOMAS = "Tomas";
    private final static String NAME_2_BO = "Bo"; // (actually existing Swedish name, with only two letters)
    private final static String NAME_3_PER = "Per";
    private final static String NAME_4_KARL = "Karl";

    private final static List<String> NAMES = Arrays.asList(
        NAME_1_TOMAS,
        NAME_2_BO,  //  2 nd name has 2 letters
        NAME_3_PER, // 3 rd name has 3 letters
        NAME_4_KARL// 4 th name has 4 letters
    );

}

Finally, I would just like to say that I do not think it is always desirable to use functions instead of interfaces with only one method.

It is indeed a nice option as in the above example when you want to reuse some pieces of code within a class, without having to create a small interface which is only intended for private use anyway.

However, it can indeed be a good idea to create an interface if it is intended to be used in public and if you can improve the readability of the code by providing the interface with good semantics.

In the above example, though, I do not think an interface (instead of a function) would have improved anything in the Scala code but rather the opposite, i.e. reduced the readability/maintainability.
For example, consider the following piece of Scala code, copied from the above exampe:

  private def getMappedList[T,U](
    elementsToBeMapped: java.util.List[T],
    elementMappingStrategy: (T => U)
  ): java.util.List[U] = {
	...

Above, the function parameter can be considered as expressive enough, i.e. it is a straight-forward translation for each element (T) from the input list into some element (U) to become included in the returned list.

In this case, the method is also used in a private context, so there is not a lot of frequent need to understand the method, i.e. the only times anyone need to understand the private method is when someone is reading the implementation of a method which invokes this private method.

In such a context, i.e. when you are reading invocation code like this:
getMappedList(stringsWithLengthsToBeReturned, (s : String) => s.length)
then it should be easy enough to understand the code, without any need for creating an interface for trying to improve the semantic/readability/maintainability.

I mean, in the corresponding Java code (see below) it was necessary to create an interface, but it was only used internally (for private refactoring purposes, i.e. to reduce duplication) and the usage of an interface did not improve the semantic, compared to the above usage of a Scala function instead of an interface.

...
    private interface ElementMappingStrategy {
        U getMappedValue(T value);
    }
...
    private <T, U> List<U> getMappedList(
        final List<T> elementsToBeMapped,
        final ElementMappingStrategy<T, U> elementMappingStrategy
    ) {
...

In fact, in this case I would rather say that the Scala method has a more intention-revealing interface, with its mapping function directly in the method signature, since with the Java interface, it is more necessary to look in two places to figure out what the method will do, i.e. you would likely want to look in the interface 'ElementMappingStrategy' instead of only looking in the method 'getMappedList' (which you can do when using a Scala function instead of an interface)

/ Tomas Johansson, Stockholm, Sweden, 2010-01-19 (updated 2010-02-12)