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();
}
};
}
}
Showing posts with label design pattern. Show all posts
Showing posts with label design pattern. Show all posts
Sunday, August 9, 2015
Saturday, July 11, 2015
Thoughts on foundational framework development
Foundational framework development is very important in any technology companies. It solves common problems shared across departments, teams or projects. It generally is lauded by management. Many great open-source frameworks, e.g. AngularJS, React, I believe, are stemmed from in-house framework development then taken from companies like Google and Facebook to public.
There are two schools of thoughts to develop foundational frameworks: bottom-up or top-down. Bottom-up approach is to build it prior to any applications by foreseeing and analyzing various potential needs of applications, finding the common area then developing it. Sometimes the analysis phase becomes a bit of guessing work and shot in the dark. And at organizational level if there is division between foundational team and application team, it could makes this approach even harder. However this approach fits well to develop a well-defined public API or standard like JDBC driver for an in-house database.
Top-down approach is more practical, teams, without the division of foundational team and application team, start to build the applications to meet business needs and both with well-defined architectures. During the course of development teams start to discover the relationship between difference layers and modules, continue to refine the architecture and code-base. At the end of project or even after project delivery teams set out to refactor and harvest a framework from the applications. This approach usually has grounded success since it is based on real-life applications to solve real problems. Also this approach is well aligned with refactoring and iterative agile development.
The following diagrams further illustrate my thoughts above:
The bottom-up approach tends to assume the interfaces between foundation layer and applications are well-defined or the boundaries can be easily discovered.
However in reality, the picture looks more like this:
So what is best way to develop foundation layer? Based on my experiences, there are few OO design principles and patterns can greatly help us.
1. Foundational interfaces should be minimum. Try not solve everything, leave as much as you can to application unless you are sure the functionality is needed. Develop a toolkit not a specific problem solver.Using java's java.util.List as example, it provides a method called get(index) and doesn't provide method getFirst() or getLast() since it leaves the clients to do that. In that way you leave the client assembles the jdk methods to any requirement it may need to accomplish.
1. Close to change, open to extension.
2. Dependency injection (IoC) or Hollywood principle - Don't call me, I call you.
3. Develop pluggable interfaces.
There are two schools of thoughts to develop foundational frameworks: bottom-up or top-down. Bottom-up approach is to build it prior to any applications by foreseeing and analyzing various potential needs of applications, finding the common area then developing it. Sometimes the analysis phase becomes a bit of guessing work and shot in the dark. And at organizational level if there is division between foundational team and application team, it could makes this approach even harder. However this approach fits well to develop a well-defined public API or standard like JDBC driver for an in-house database.
Top-down approach is more practical, teams, without the division of foundational team and application team, start to build the applications to meet business needs and both with well-defined architectures. During the course of development teams start to discover the relationship between difference layers and modules, continue to refine the architecture and code-base. At the end of project or even after project delivery teams set out to refactor and harvest a framework from the applications. This approach usually has grounded success since it is based on real-life applications to solve real problems. Also this approach is well aligned with refactoring and iterative agile development.
The following diagrams further illustrate my thoughts above:
The bottom-up approach tends to assume the interfaces between foundation layer and applications are well-defined or the boundaries can be easily discovered.
So bottom-up approach ends up like this:
What happens above is that application logics are everywhere in the foundation layer. In the code we see lots of if/else, case/switch statements, and any changes in foundation layer to accommodate one application will impact other applications. That foundational layer eventually becomes a monolithic application.
1. Foundational interfaces should be minimum. Try not solve everything, leave as much as you can to application unless you are sure the functionality is needed. Develop a toolkit not a specific problem solver.Using java's java.util.List as example, it provides a method called get(index) and doesn't provide method getFirst() or getLast() since it leaves the clients to do that. In that way you leave the client assembles the jdk methods to any requirement it may need to accomplish.
1. Close to change, open to extension.
2. Dependency injection (IoC) or Hollywood principle - Don't call me, I call you.
3. Develop pluggable interfaces.
Subscribe to:
Posts (Atom)