32
Java Generics – Chapter 9: Design patterns /** * INF329 - Spring 2007 * Presented by Stian Stokkenes * [email protected] */ {

Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

  • Upload
    others

  • View
    13

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

Java Generics – Chapter 9: Design patterns/*** INF329 - Spring 2007* Presented by Stian Stokkenes* [email protected]*/ {

Page 2: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

2

Chapter 9 - Design patterns• Goal: Show how design patterns can take advantage of

Java generics• Viewing the following patterns:

– Visitor pattern– Interpreter pattern– Function pattern– Strategy pattern– Subject- / Observer pattern

Page 3: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

3

9.1 – Visitor pattern• Data structure often defined by case analysis and

recursion• A binary tree of type Tree<E> is

– A leaf containing a single value of type E– A branch containing a left and a right sub tree of types E

• Represented in OO language by abstract class– Each case by subclass– Abstract class declares abstract method for all possible

operation– Subclasses implements the methods

Page 4: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

4

ex.1 – Simple tree and clientabstract class Tree<E>{

abstract public String toString();abstract public double sum();public static <E> Tree<E> leaf(final E e){

return new Tree<E>(){public String toString(){return e.toString();}public Double sum(){return ((Number)e).doubleValue();}

};}public static <E> Tree<E> branch(final Tree<E> l, final Tree<E> r){

return new Tree<E>(){public String toString(){

return “(“+l.toString()+”^”+r.toString()+”)”;}public Double sum(){return l.sum() + r.sum();}

};}class TreeClient{

public static void main(String[] args){Tree<Integer> t = Tree.branch(Tree.branch(Tree.leaf(1),

Tree.leaf(2)),Tree.leaf(3));assert t.toString().equals(“((1^2)^3)”);assert t.sum() == 6;

}}

}

Page 5: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

5

9.1 – Visitor pattern• Difficult to know all operations required on data

structure– Different developers responsible for definition classes and

client classes of the definition classes

• Visitor pattern -> possible to provide new operations without modifying the classes that defines the data structure– Abstract class declares visit method. Visitor as argument– Visitor implements interface that specifies one method for

each case in the specification of the structure– Subclasses implement visit method by calling method of the

visitor for the corresponding case.

Page 6: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

6

ex.2 – Tree with visitorsabstract class Tree<E> {

public interface Visitor<E, R>{public R leaf(E elt);public R branch(R left, R right);

}public abstract <R> R visit(Visitor<E, R> v);public static <T> Tree<T> leaf(final T e){

return new Tree<T>(){public <R> R visit(Visitor<T, R> v){return v.leaf(e);}

};}public static <T> Tree<T> branch(final Tree<T> l, final Tree<T> r){

return new Tree<T>(){public <R> R visit(Visitor<T, R> v){

return v.branch(l.visit(v), r.visit(v));}

};}

}

Page 7: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

7

9.1 – Visitor pattern• Abstract class Tree<E> only one abstract method• Interface Visitor<E, R> specifies two method

– leaf• Accepts a value of type E• Returns value of type R

– branch • Accept two values of type R• Returns value of type R

• Subclasses implements visit– leaf

• by invoking leaf method of visitor on element in leaf– branch

• By invoking branch method of visitor of the visitor on the result of recursive calls of the visitor on the left and right subtrees.

Page 8: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

8

9.1 – Visitor pattern• Without generics

– each visitor would have to return a result of type Object– many additional casts would be required– visitors often designed to not return a value– result often accumulated in variable local to visitor– complicating flow of data through the program

• With generics – each visitor has two type parameters

• One for the element type of the tree• One for the return type of the visitor

Page 9: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

9

ex.3 – Client with visitorsclass treeClient {

public static <T> String toString(Tree<T> t){return t.visit(new Tree.Visitor<T, String>(){

public String leaf(T e){ return e.toString(); }public String branch(String l, String r){

return “(“ + l + ”^” + r + ”)”; }

});}public static <N extends Number> double sum(Tree<N> t){

return t.visit(new Tree.Visitor<N, Double>() {public Double leaf(N e){ return e.doubleValue(); }public Double branch(Double l, Double r){ return l+r; }

});}public static void main(String[] args){

Tree<Integer> t = Tree.branch(Tree.branch(Tree.leaf(1),Tree.leaf(2)),Tree.leaf(3));

assert toString(t).equals(“((1^2)^3)”);assert sum(t) == 6;

}}

Page 10: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

10

9.1 – Visitor pattern• Sum method more precise with visitors• With simple trees

– Sum need type signature to indicate that it works on any element type

– Requires cast to convert leaf to Number– Class cast error is raised at run time if sum invoked on tree

without numbers

• With visitors– Sum method may have type signature that indicate it works

only on elements that are numbers• No cast required• Type error reported at compile time

Page 11: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

11

9.2 – Interpreter pattern• One use of trees is to represent expressions in a

programming language– Represented by abstract class– Each expression represented by subclass– Abstract method to evaluate an expression– Each subclass implements the method as appropiate for the

coreresponding expression

• With generics– Possible to parameterize expression type by type of

expression, e.g. Exp<Integer>, Exp<Pair<Integer, Integer>>

Page 12: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

12

ex.4 – Interpreter with genericsclass Pair<A,B> {

private final A left;private final B right;public Pair(A l, B r){ left = l; right = r;}public A left(){ return left; }public B right(){ return right; }

}abstract class Exp<T> {

abstract public T eval();static Exp<Integer> lit(final int i){

return new Exp<Integer>(){ public Integer eval(){ return i; }};}static Exp<Integer> plus(final Exp<Integer> e1, final Exp<Integer> e2){

return new Exp<Integer>(){ public Integer eval(){return e1.eval()+e2.eval();

}}; }static <A, B> Exp<Pair<A,B>> pair(final Exp<A> e1, final Exp<B> e2){

return new Exp<Pair<A, B>>(){ public Pair<A, B> eval(){return new Pair<A,B>(e1.eval(),e2.eval());

} };}static <A, B> Exp<A> left(final Exp<Pair<A, B>> e){

return new Exp<A>(){ public A eval() { return e.eval().left();} };}static <A, B> Exp<B> right(final Exp<Pair<A, B>> e){

return new Exp<B>(){ public B eval() { return e.eval().right();} };}public static void main(String[] args){

Exp<Integer> e = left(pair(plus(lit(3),lit(4)),lit(5)));assert e.eval() == 7;

}}

Page 13: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

13

9.2 – Interpreter pattern• In example:

– Integer literal of type Exp<Integer>– Sum expression of type Exp<Integer>,

• with two sub expressions of type Exp<Integer>– Expression to construct pair of type Exp<Pair<A, B>>

• with two sub expressions of type Exp<A> and Exp<B>– Expression to select the left component of a pair of type

Exp<A>• with subexpression of type Exp<Pair<A, B>>

– Expression to select the left component of a pair of type Exp<B>

• with subexpression of type Exp<Pair<A, B>>

Page 14: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

14

9.3 – Function pattern• Converts an arbitrary method into an object• Relation between an function and the corresponding

method similar to relation between Comparator and compareTo method

• Generic version demonstrate how to use type variable in throws clause of a method declaration.– Useful when different instances of a class contain methods

that may be useful when different instances of a class contain methods that may raise different checked exceptions

• See example 5.

Page 15: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

15

9.3 – Function pattern• Example defines class, Function <A, B, X>, which

represent a function– Class contains abstract method, apply

• Accepts argument of type A• Returns result of type B• May throw exception of type X

– Class contains applyAll method• Accept argument of type List<A>• Returns result of type List<B>• May throw exception of type X• Invokes apply on each element in argument list to produce

result list

Page 16: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

16

9.3 – Function pattern• Main method defines three objects

1. length of type Function<String, Integer, Error>– Accepts String– Returns integer (Length of given String)– Since it raises no checked exceptions, the third is set to error (Could be

set to RuntimeException)2. forName of type

Function<String, Class<?>, ClassnotFoundException>– Accepts String– Returns a class named by given String– Apply may throw ClassNotFoundException, which is given as the

third parameter3. getRunMethod of type Function<String, Method, Exception>

– Accepts String– Returns a method named run in the class named by the given String– Body of the method might raise a ClassNotFoundException or

NoSuchMethodException, so third parameter is taken to be Exception.

Page 17: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

17

9.3 – Function pattern• Last example shows chief limitations of giving

generics types to exception– Often no suitable class or interface that contains all

exceptions the function may raise.– Forced to fall back on Exception

• To general to provide useful information

Page 18: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

18

9.3 – Function pattern% Typical run of code in example 5:

% java Functions java.lang.Thread java.lang.Runnable[16, 18][class java.lang.Thread, interface java.lang.Runnable][public void java.lang.Thread.run(), public abstract void java.lang.Runnable.run()]

% java Functions java.lang.Thread java.util.List[16, 14][class java.lang.Thread, interface java.util.List]java.lang.NoSuchMethodException: java.util.List.run()

% java Functions java.lang.Thread Inf329[16, 6]java.lang.ClassNotFoundException: Inf329java.lang.ClassNotFoundException: Inf329

Page 19: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

19

9.4 – Strategy pattern• Used to decouple a method from an object

– Allowing to supply many possible instances of the method

• Illustrated by considering how tax payers may apply different tax strategies– A hierarchy for tax payers and a related hierarchy for tax

strategies• One default strategy applies to any tax payer• One subclass of tax payer is a trust• One subclass of default strategy applies only to trust

Page 20: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

20

9.4 – Strategy pattern• Example illustrate use of type variables with

recursive bounds– Applied to clarify the connection between tax payer and

associated tax strategies

• Example also illustrate use of getThis, which allows programmer to assign more precise type to this when type variables with recursive bounds appear

• See example 6.

Page 21: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

21

9.4 – Strategy pattern• Generics allows

– programmer to specialize a given tax strategy to a given type of tax payer

– compiler to detect when a strategy is applied to the wrong type of tax payer

– e.g trustStrategy.computeTax(person); //compile-time error

• Without generics computeTax would have to accept an argument of type TaxPayer and cast it to trust, and exception would be thrown at run-time

Page 22: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

22

9.4 – Strategy pattern• Example illustrates structuring technique of parallel

class hierarchies– One class hierarchy consist of TaxPayer, Person and Trust– A parallel class hierarchy consist of strategies corresponding

to each off these• DefaultTaxStrategy and DodgingTaxStrategy apply to any

Taxpayer• No specialized strategy apply to Person• TrustTaxStrategy specialized for Trust

Page 23: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

23

9.4 – Strategy pattern• Usually, some connection between such parallel

hierarchies– In this case, computeTax method for a TaxStrategy that is

parallel to a given Taxpayer expects an argument that is of the corresponding type

– e.g. computeTax method for TrustTaxStrategy expects argument of type Trust

• With generics, this connection can be captured in the types themselves– e.g. TaxStrategy<P> expects an argument of type P, where

P must be subclass of TaxPayer– By using this technique, generics can often be used to

capture similar relations in other parallel hierarchies

Page 24: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

24

9.4 – Strategy pattern• In example, instead of this we use a method called getThis

– Used because this doesn’t have type P– e.g. Person extends TaxPayer<Person>, so P is the same as

Person within the class– In fact, this will have the same type as P, but the compiler doesn't

know that – Returns the same value as this, but gives it the type P

• Declared abstract in the base class– Because it declares the type for getThis, but doesn't declare the

body– Body declared in the final subclasses Person and Trust

• Useful in situations whenever one wants to use this in the base type with more specific type provided by the type parameter

Page 25: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

25

9.5 – Subject-Observer pattern• Like strategy pattern, the Subject-Observer (SO)

pattern uses parallel class hierarchies, but requires two type variables with recursive bounds– One to stand for the specific kind of subject– One to stand for the specific kind of observer

• The java library implements a nongeneric version of SO in the package java.util with the class Observable and the interface Observer– Observable class contains methods to register observers, to

indicate that observable has changed and to notify all observers of any changes among others

Page 26: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

26

9.5 – Subject-Observer pattern• The Observer interface specifies the update method that is called

by notifyObservers– Takes two parameters

1. Observable – The subject that has changed2. Object – The broadcast argument

• The appearance of Object indicates an opportunity to generify– Should be generefied by adding type parameter A, corresponding to

the argument type• Observable and Observer can be replaced with type parameters

S and O– Within the update method of the observer, the programmer may

call on any method supported by the subject S without first requiriga cast

Page 27: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

27

9.5 - Subject-Observer pattern/* Observable and observer before generics */

package java util;public class Observable {

pulic void addObserver(Observer O){…}protected void clearChanged(){…}public int countObservers(){…}public void deleteObserver(Observer O){…}public boolean hasChanged(){…}public void notifyObservers(){…}public boid notifyObservers(Object arg){…}protected void setChanged(){…}

}package java.util;public interface Observer {

public void update(Observable o, Object arg);}

Page 28: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

28

9.5 - Subject-Observer pattern/* Observable with generics */

package java.util;class StubException extends UnsupportedOperationException {}public class Observable<S extends Observable<S, O, A>,

O extends Observer<S, O, A>, A>{

public void addObserver(O o){throw new StubException();}protected void clearChanged(){ throw new StubException();}public int countObservers(){ throw new StubException();}public void deleteObserver(O o){throw new StubException();}public boolean hasChanged(){throw new StubException();}public void notifyObservers(){throw new StubException();}public void notifyObservers(A a){

throw new StubException();}protected void setChanged(){throw new StubException();}

}

Page 29: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

29

9.5 - Subject-Observer pattern/* Observer with generics */

package java.util;public interface Observer<S extends Observable<S, O, A>,

O extends Observer<S, O, A>, A>{

public void update(S o, A a);}

Page 30: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

30

9.5 – Subject-Observer pattern• As an example, a currency converter is presented• The converter allows to enter conversion rates for

three currencies– Changing the entry for a rate, causes the corresponding value

to be recomputed– Changing the entry for a value, causes all the values to be

recomputed

• The client instantiates the pattern by – Declaring CModel to be a subclass of Observable– Declaring CView to be a subinterface of Observer– Instantiates the argument type to Currency

Page 31: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

31

9.5 – Subject-Observer pattern• CModel class invokes the update method of RateView

whenever a rate is changed,– Passing the corresponding currency as the argument

• Invokes the update method of ValueView whenever a value is changed, passing null as the argument

• See example 7.

Page 32: Java Generics – Chapter 93 9.1 – Visitor pattern • Data structure often defined by case analysis and recursion • A binary tree of type Tree is – A leaf containing

}//End of chapter 9