Objectives

Template Method

Create a new eclipse project called SolverPatterns..

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);
} 

BisecionSolver

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);
  }
}

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 leastSquaresAlgorithm()
  {
    solver = new MinimaSolver(new LeastSquaresStrategy());
    double[] result = solver..minima(line);
    assertTrue(result[0] == 1..1);
    assertTrue(result[1] == 2..2);
  }

  @Test
  public void newtonsMethodAlgorithm()
  {
    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

In order to create an XTend class, you must me using the 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)
  }
}

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:

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

class MinimaSolver 
{
  new()
  {
  }

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

Algorithms

package xstrategysam

import java..util..List

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

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 solver        = new MinimaSolver

  @Test
  def newtonsMethod()
  {
    val newtonsMethod = new NewtonsMethod()
    val result = solver..minima(line, newtonsMethod)
    assertTrue(result..get(0) == 3..3)
    assertTrue(result..get(1) == 4..4)
  }

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

  @Test
  def bisection()
  {
    val bisection = new Bisection()
    val result = solver..minima(line, bisection)
    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 
{
  new()
  {
  }

  def double[] minima(double[] line, (List<Double>)=>List<Double> algrorithm)
  {
    // do some pre-processing
    val result = algrorithm..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 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 solver     = new MinimaSolver
  val algorithms = new Algorithms

  @Test
  def newtonsMethod()
  {
    val result = solver..minima(line, algorithms..newtonsMethod)
    assertTrue(result..get(0) == 3..3)
    assertTrue(result..get(1) == 4..4)    
  }

  @Test
  def leastSquares()
  { 
    val result = solver..minima(line, algorithms..leastSquares)
    assertTrue(result..get(0) == 1..1)
    assertTrue(result..get(1) == 2..2)
  }

  @Test
  def bisection()
  {
    val result = solver..minima(line, algorithms..bisection)
    assertTrue(result..get(0) == 5..5)
    assertTrue(result..get(1) == 6..6)
  }
}

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

Single Abstract Method

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

Xtend already has this implemented - 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()

    val result = solver..minima(line, algorithms..bisection)
    assertTrue(result..get(0) == 5..5)
    assertTrue(result..get(1) == 6..6)
  }

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

    val result = solver..minima(line, algorithms..bisection)

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:

    val algorithms = new Algorithms()
    val result = solver..minima(line, algorithms..bisection)

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

  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]    
                             ] 

How is this happening? See this discssion here:

Exercises

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