Upload
others
View
13
Download
0
Embed Size (px)
Citation preview
Java Generics – Chapter 9: Design patterns/*** INF329 - Spring 2007* Presented by Stian Stokkenes* [email protected]*/ {
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
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
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;
}}
}
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.
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));}
};}
}
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.
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
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;
}}
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
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>>
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;
}}
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>>
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.
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
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.
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
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
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
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.
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
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
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
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
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
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
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);}
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();}
}
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);}
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
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.
}//End of chapter 9