Review the Java variants of the template method and strategy patterns. Reoode these in Xtend. Reimplement Strategy using Lambdas in Xtend
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:
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);
}
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};
}
}
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};
}
}
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};
}
}
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
Create a package in the same package called strategy
- and bring in these classes:
package strategy;
public interface FindMinima
{
double[] algorithm(double[] line);
}
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;
}
}
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};
}
}
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};
}
}
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};
}
}
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 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:
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]
}
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:
Have a close look at each of these classes, and note any differences from the xtend versions..
Create a new package called xstrategysam
and create the following xtend classes:
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
}
}
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:
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.
Create a new package called xstrategy
and create the following xtend classes:
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
}
}
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:
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..
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:
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
Java 8 now also has Lambda Expressions:
Using the above sources, recode step 05 - the xtend strategy implemented using labmdas - into Java 8.