Sunday, August 9, 2015

Generics Design Patterns: part II

Generics were introduced Java 5. First of all, recap of the benefit of Generics. 1) compile-time type safety; 2) eliminate type casting; 3) enable generics algorithm/design pattern implementation.

Continuing from previous blog: generic related design patterns. In this blog I list couple more generics-based design patterns. Please note many of the examples are from the book "Java Generics and Collections" with some modifications.

1. Function design pattern.

Function design pattern allows functions become object and passed around. In below code snippet, a generics interface was used to define a function which takes T type input, return R type and throws X which is subclass of Throwable.

public class FunctionQ {

    interface Function<T, R, X extends Throwable> {
        R apply(T t) throws X;
    }
   
    static <T, R, X extends Throwable>  List<R> applyAll(Function<T, R, X> f, List<T> list) throws X {
        List<R> result = new ArrayList<R>(list.size());
        for(T t : list){
            result.add(f.apply(t));
        }
        return result;
    }
}


2. Strategy design pattern

One of the common patterns in software development is there are a set of domain classes form parent-child class hierarchy, then there are a set of business logic classes which are parallel to the domain classes, also for parent-child class hierarchy and operate on those domain classes.

In the following snippet, we have abstract base class TaxPayer and Individual/Organization extends from this base class. Then we have different tax calculators implement different tax compute strategies for different classes.

public class StrategyQ {

    abstract class TaxPayer {
        float income;
        float getIncome(){
            return income;
        }
    }
   
    class Individual extends TaxPayer {
       
    }
   
    class Organization extends Individual {
        boolean isProfit;
       
    }
   
    abstract class TaxCalculator<P extends TaxPayer> {
        abstract double computeTax(P p);
    }
   
    class DefaultTaxCalculator<P extends TaxPayer> extends TaxCalculator<P>{
        private final double RATE = 0.4;
        @Override
        double computeTax(P p) {
            return p.getIncome() * RATE;
        }       
    }
   
    class CheatingTaxCalculator<P extends TaxPayer> extends TaxCalculator<P>{
        @Override
        double computeTax(P p) {
            return 0;
        }       
    }
   
    class OrganizationTaxCalculator extends DefaultTaxCalculator<Organization>{
        @Override
        double computeTax(Organization p) {
            return p.isProfit?super.computeTax(p):0;
        }   
    }
}

3. Visitor pattern

The visitor pattern allows the decoupling of domain class and its operations. One of the common usage of visitor pattern is to define the set of operations on tree node. Below is the code: 

abstract static class Tree<E> {
        /*
         * list of Tree operations
         */
        abstract public String toString();
        abstract public Double sum();
       
        /*
         * static creator methods
         */
        public static <E> Tree<E>  leaf(final E e){
            return new Tree<E>(){

                @Override
                public String toString() {
                    return e.toString();
                }

                @Override
                public Double sum() {
                    return ((Number)e).doubleValue();
                }
               
            };
        }
       
        public static <E> Tree<E> branch(final Tree<E> left, final Tree<E> right){
            return new Tree<E>(){
            @Override
            public String toString() {
                return "(" + left.toString() + "^" + right.toString() + ")";
            }

            @Override
            public Double sum() {
                return left.sum() + right.sum();
            }
            };
        }
       
    }

No comments:

Post a Comment