Objectives

Review the Java variants of the template method and strategy patterns. Reoode these in Xtend. Reimplement Strategy using Lambdas in Xtend

Template Method

Assuming you have a recent version of Eclipse installed, create a new eclipse project called solver-patterns.

Create a package called templatemethod - and bring in these classes:

MinimaSolver

package templatemethod;

public abstract class MinimaSolver
{
  public MinimaSolver()
  {
  }

  double[] minima(double[] line)
  {
    // do some pre-processing
    double[] result = null;
    result = algorithm(line);
    // do some post-processing
    return result;
  }

  public abstract double[] algorithm(double[] line);
}

BisectionSolver

package templatemethod;

public class BisectionSolver extends MinimaSolver
{
  public double[] algorithm(double[] line)
  {
    // Compute Minima on line
    //  - algorithm
    double x = 5.5;  // simulated result
    double y = 6.6;  // simulated result

    return new double[]{x, y};
  }
}

LeastSquaresSolver

package templatemethod;

public class LeastSquaresSolver extends MinimaSolver
{
  public double[] algorithm(double[] line)
  {
    // Compute Minima on line
    //  - algorithm
    double x = 1.1;  // simulated result
    double y = 2.2;  // simulated result

    return new double[]{x, y};
  }
}

NewtonsMethodSolver

package templatemethod;

public class NewtonsMethodSolver extends MinimaSolver
{
  public double[] algorithm(double[] line)
  {
    // Compute Minima on line
    //  - algorithm
    double x = 3.3;  // simulated result
    double y = 4.4;  // simulated result

    return new double[]{x, y};    
  }
}

MinimaSolverTest

package templatemethod;

import static org.junit.Assert.*;
import org.junit.Test;

public class MinimaSolverTest
{
  private double[] line = { 1.0, 2.0, 1.0, 2.0, -1.0, 3.0, 4.0, 5.0, 4.0 };
  private MinimaSolver solver;

  @Test
  public void leastSquaresAlgorithm()
  {
    solver = new LeastSquaresSolver();
    double[] result = solver.minima(line);
    assertTrue(result[0] == 1.1);
    assertTrue(result[1] == 2.2);
  }

  @Test
  public void newtonsMethodAlgorithm()
  {
    solver = new NewtonsMethodSolver();
    double[] result = solver.minima(line);
    assertTrue(result[0] == 3.3);
    assertTrue(result[1] == 4.4);
  }

  @Test
  public void bisection()
  {
    solver = new BisectionSolver();
    double[] result = solver.minima(line);
    assertTrue(result[0] == 5.5);
    assertTrue(result[1] == 6.6);
  }
}

(You will need to include JUint libraries for the above to compile)

Verify that the tests pass

Strategy

Create a package in the same package called strategy - and bring in these classes:

FindMinima

package strategy;

public interface FindMinima
{
  double[] algorithm(double[] line);
}

MinimaSolver

package strategy;

public class MinimaSolver
{
  private FindMinima strategy;

  public MinimaSolver(FindMinima strategy)
  {
    this.strategy = strategy;
  }

  double[] minima(double[] line)
  {

    // do some pre-processing
    double[] result = null;

    result = strategy.algorithm(line);

    // do some post-processing
    return result;
  }

  public void changeStrategy(FindMinima newStrategy)
  {
    strategy = newStrategy;
  }
}

BisecionStrategy

package strategy;

public class BisectionStrategy implements FindMinima
{
  public double[] algorithm(double[] line)
  {
    // Compute Minima on line
    //  - algorithm
    double x = 5.5;  // simulated result
    double y = 6.6;  // simulated result

    return new double[]{x, y};
  }
}

LeastSquaresStrategy

package strategy;

public class LeastSquaresStrategy implements FindMinima
{
  public double[] algorithm(double[] line)
  {
    // Compute Minima on line
    //  - algorithm
    double x = 1.1;  // simulated result
    double y = 2.2;  // simulated result

    return new double[]{x, y};
  }
}

NewtonsMethodStrategy

package strategy;

public class NewtonsMethodStrategy implements FindMinima
{
  public double[] algorithm(double[] line)
  {
    // Compute Minima on line
    //  - algorithm
    double x = 3.3;  // simulated result
    double y = 4.4;  // simulated result

    return new double[]{x, y};  
  }
}

MinimaSolverTest

package strategy;

import org.junit.Test;
import static org.junit.Assert.*;

public class MinimaSolverTest
{
  private double[] line = {1.0, 2.0, 1.0, 2.0, -1.0, 3.0, 4.0, 5.0, 4.0};
  private MinimaSolver solver;

  @Test
  public void leastSquares()
  {
    solver = new MinimaSolver(new LeastSquaresStrategy());
    double[] result = solver.minima(line);
    assertTrue(result[0] == 1.1);
    assertTrue(result[1] == 2.2);
  }

  @Test
  public void newtonsMethod()
  {
    solver = new MinimaSolver(new NewtonsMethodStrategy());
    double[] result = solver.minima(line);
    assertTrue(result[0] == 3.3);
    assertTrue(result[1] == 4.4);
  }

  @Test
  public void bisection()
  {
    solver = new MinimaSolver(new BisectionStrategy());
    double[] result = solver.minima(line);
    assertTrue(result[0] == 5.5);
    assertTrue(result[1] == 6.6);
  }

  @Test
  public void testChangeAlgorithm()
  {
    solver = new MinimaSolver(new LeastSquaresStrategy());

    double[] result = solver.minima(line);
    assertTrue(result[0] == 1.1);
    assertTrue(result[1] == 2.2);
    solver.changeStrategy(new BisectionStrategy());

    result = solver.minima(line);
    assertTrue(result[0] == 5.5);
    assertTrue(result[1] == 6.6);
  }
}

Verify that the tests pass

Xtend TemplateMethod

Xtend is one the potential successors to Java (others include Scala and Kotlin). Xtend has some advantages over these, not least its simplicity and robust implementation. We will review xtend on next weeks lab, but you can try the experiments in the next few steps in order to gain an initial familiarity.

You must be using Eclipse for DSL Developers distribution, or install XTend into your version of eclipse.

In the same project, create a new package called xtemplatemethod. Using the eclipse context menu, create an XTend class called MinmaSolver. Once you have created the class, eclipse will generate an error if the XTend libraries are not included.. Selecting the error in the IDE, and selecting autocorrect may be the easies way of having the libraries included.

We can place multiple classes in a single source file in XTend:

MinimaSolver

package xtemplatemethod

abstract class MinimaSolver 
{
  new()
  {
  }

  def double[] minima(double[] line)
  {
    // do some pre-processing
    var double[] result = null
    result = algorithm(line)
    // do some post-processing
    result
  }

  def abstract double[] algorithm(double[] line);
}

class BisectionSolver extends MinimaSolver
{
  override algorithm(double[] line) 
  {
    // Compute Minima on line
    //  - algorithm
    val x = 5.5;  // simulated result
    val y = 6.6;  // simulated result
    #[x, y]
  }
}

class NewtonsMethodSolver extends MinimaSolver
{
  override algorithm(double[] line) 
  {
    // Compute Minima on line
    //  - algorithm
    val x = 3.3;  // simulated result
    val y = 4.4;  // simulated result
    #[x, y]    
  }
}

class LeastSquaresSolver extends MinimaSolver
{
  override algorithm(double[] line) 
  {
    // Compute Minima on line
    //  - algorithm
    val x = 1.1;  // simulated result
    val y = 2.2;  // simulated result
    #[x, y]     
  }

MinimaSolverTest

package xtemplatemethod

import static org.junit.Assert.*
import org.junit.Test

class MinimaSolverTest
{
  val line = #[ 1.0, 2.0, 1.0, 2.0, -1.0, 3.0, 4.0, 5.0, 4.0 ]
  var MinimaSolver solver

  @Test
  def newtonsMetod()
  {
    solver = new NewtonsMethodSolver
    val result = solver.minima(line)
    assertTrue(result.get(0) == 3.3)
    assertTrue(result.get(1) == 4.4)
  }

    @Test
  def leastSquares()
  {
    solver = new LeastSquaresSolver
    val result = solver.minima(line)
    assertTrue(result.get(0) == 1.1)
    assertTrue(result.get(1) == 2.2)
  }
  @Test
  def bisection()
  {
    solver = new BisectionSolver
    val result = solver.minima(line)
    assertTrue(result.get(0) == 5.5)
    assertTrue(result.get(1) == 6.6)
  }
}

You may need to manually add 'XTend Library' to the project for the above to compile. To do this, select the Project in Package Explorer and then select the context menu. From this menu select 'Build Path' and from there locate the 'Add Library' dialog. 'Xtend Library' will be available here if Xtend is installed correctly.

Verify that these tests pass..

If there are no errors, eclipse will have generated a source folder called 'xtend-gen' in the project, with a matching set of packages for the xtend sources.. Explore the generated 'xtemplatemethod' package, you should see the java version of:

  • BisectionSolver
  • NewtonsMethodSolver
  • LeastSquaresSolver
  • MinimaSolver
  • MinimaSolverTest

Have a close look at each of these classes, and note any differences from the xtend versions..

XTend Strategy SAM

Create a new package called xstrategysam and create the following xtend classes:

MinimaSolver

package xstrategysam

import java.util.List

public interface FindMinima
{
  def List<Double> algorithm(List<Double>line)
}

class MinimaSolver 
{
  private FindMinima findMinima

  new(FindMinima findMinima)
  {
    this.findMinima = findMinima
  }

  def double[] minima(double[] line)
  {
    // do some pre-processing
    val result = findMinima.algorithm(line)
    // do some post-processing
    result
  }
}

Algorithms

package xstrategysam

import java.util.List

public class Bisection implements FindMinima
{
  override List<Double> algorithm(List<Double>line)
  {
    return #[5.5, 6.6]
  }
}

public class NewtonsMethod implements FindMinima
{
  override List<Double> algorithm(List<Double>line)
  {
    return #[3.3, 4.4]
  }
}

public class LeastSquares implements FindMinima
{
  override List<Double> algorithm(List<Double>line)
  {
    return #[1.1, 2.2]
  }
}

Verify that the following tests pass:

MinimaSolverTest

package xstrategysam

import static org.junit.Assert.*
import org.junit.Test

class MinimaSolverTest
{
  val line          = #[ 1.0, 2.0, 1.0, 2.0, -1.0, 3.0, 4.0, 5.0, 4.0 ]
  var MinimaSolver solver        

  @Test
  def leastSquares()
  { 
    solver = new MinimaSolver (new LeastSquares)
    val result = solver.minima(line)
    assertTrue(result.get(0) == 1.1)
    assertTrue(result.get(1) == 2.2)
  }

  @Test
  def newtonsMethod()
  {
   solver = new MinimaSolver (new NewtonsMethod)
    val result = solver.minima(line)
    assertTrue(result.get(0) == 3.3)
    assertTrue(result.get(1) == 4.4)
  }

  @Test
  def bisection()
  {
    solver = new MinimaSolver (new Bisection)
    val result = solver.minima(line)
    assertTrue(result.get(0) == 5.5)
    assertTrue(result.get(1) == 6.6)
  }
}

Explore the generated java sources for the above xtend classes.

XTend Strategy

Create a new package called xstrategy and create the following xtend classes:

MinimaSolver

package xstrategy

import java.util.List

class MinimaSolver 
{
  public (List<Double>)=>List<Double> findMinima

  new((List<Double>)=>List<Double> findMinima)
  {
    this.findMinima = findMinima
  }

  def List<Double> minima(double[] line)
  {
    // do some pre-processing
    val result = findMinima.apply(line)
    // do some post-processing
    result
  }
}

Algorithms

package xstrategy

import java.util.List

class Algorithms 
{  
  public val bisection     = [ List<Double> line | 
                                // Compute Minima on line
                                //  - algorithm
                                val x = 5.5  // simulated result
                                val y = 6.6  // simulated result
                                #[x, y]    
                             ]  

  public val newtonsMethod = [ List<Double> line | 
                                // Compute Minima on line
                                //  - algorithm
                                val x = 3.3  // simulated result
                                val y = 4.4  // simulated result
                                #[x, y]    
                             ]  

  public val leastSquares = [ List<Double> line | 
                                // Compute Minima on line
                                //  - algorithm
                                val x = 1.1  // simulated result
                                val y = 2.2  // simulated result
                                #[x, y]    
                             ]     
}

Verify that the following tests pass:

MinimaSolverTest

package xstrategy

import java.util.ArrayList
import java.util.List
import org.junit.Test
import xstrategysam.Bisection

import static org.junit.Assert.*

class MinimaSolverTest
{
  val line       = #[ 1.0, 2.0, 1.0, 2.0, -1.0, 3.0, 4.0, 5.0, 4.0 ]
  val algorithms = new Algorithms

  @Test
  def newtonsMethod()
  {
    var solver = new MinimaSolver (algorithms.newtonsMethod)
    val result = solver.minima(line)
    assertTrue(result.get(0) == 3.3)
    assertTrue(result.get(1) == 4.4)    
  }

  @Test
  def leastSquares()
  { 
    var solver = new MinimaSolver (algorithms.leastSquares)
    val result = solver.minima(line)
    assertTrue(result.get(0) == 1.1)
    assertTrue(result.get(1) == 2.2)
  }

  @Test
  def bisection()
  {
    var solver = new MinimaSolver (algorithms.bisection)
    val result = solver.minima(line)
    assertTrue(result.get(0) == 5.5)
    assertTrue(result.get(1) == 6.6)
  }

  @Test
  def algorithmList()
  {
    var List <(List<Double>)=>List<Double>> list = new ArrayList

    list.add(algorithms.bisection)
    list.add(algorithms.newtonsMethod)
    list.add(algorithms.leastSquares)

    for ((List<Double>)=>List<Double> algorithm : list)
    {
      algorithm.apply(line)
    }
  }
}

Explore the generated java sources for the above xtend classes..

Single Abstract Method

SAM optimisation and lambda conversion is an interesting topic in Java 8:

Xtend supports this feature - as we can see with a simple experiment. Going back to the xstrategysam package, include this extra test:

  @Test
  def SAM()
  { 
    val algorithms = new Algorithms()

    solver = new MinimaSolver (algorithms.bisection);
    val result = solver.minima(line)
    assertTrue(result.get(0) == 5.5)
    assertTrue(result.get(1) == 6.6)
  }

You will need to import the Algorightms class from the xstrategy package:

import xstrategy.Algorithms

This looks innocent, but relfect again on what is happening here:

    solver = new MinimaSolver (algorithms.bisection);
    val result = solver.minima(line);

The solver being used here is this one:

class MinimaSolver 
{
  new()
  {
  }

  def double[] minima(double[] line, FindMinima findMinima)
  {
    // do some pre-processing
    val result = findMinima..algorithm(line)
    // do some post-processing
    result
  }
}

... which expects an object implementing FindMinima interface. However, we are passing an lambda like this:

  public val bisection     = [ List<Double> line | 
                                // Compute Minima on line
                                //  - algorithm
                                val x = 5.5  // simulated result
                                val y = 6.6  // simulated result
                                #[x, y]    
                             ]

which has, in fact, nothing to do with the FindMinima interface:

Repo for this lab<

Exercise 1

Examine the following:

    var List <(List<Double>)=>List<Double>> list = new ArrayList

    list.add(algorithms.bisection)
    list.add(algorithms.newtonsMethod)
    list.add(algorithms.leastSquares)

What is going on in the above fragment?

Turn this fragment into a unit test - and incorporate into the xtrategy package

Exercise 2:

Java 8 now also has Lambda Expressions:

Using the above sources, recode step 05 - the xtend strategy implemented using labmdas - into Java 8.