About Calen Legaspi

Calen is CEO and founder of Orange & Bronze Software Labs (http://orangeandbronze.com), an outsourcing, consulting & training firm based in Manila, specializing in Agile Software Development, Spring Framework and Grails.

Template Method Pattern Example Using Java Generics

If you find that a lot of your routines are exactly the same except for certain sections, you might want to consider the Template Method to eliminate error-prone code duplication. Here’s an example: Below are two classes that do similar things:
 
 
 
 
 
 
 
 

  1. Instantiate and initialize a Reader to read from a CSV file.
  2. Read each line and break it up into tokens.
  3. Unmarshal the tokens from each line into an entity, either a Product or a Customer.
  4. Add each entity into a Set.
  5. Return the Set.

As you can see, it’s only in the third step that there’s a difference – unmarshalling to one entity or another. All other steps are the same. I’ve highlighted the line where the code is different in each of the snippets.

ProductCsvReader.java

public class ProductCsvReader {
 
    Set<Product> getAll(File file) throws IOException {
        Set<Product> returnSet = new HashSet<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(file))){
            String line = reader.readLine();
            while (line != null && !line.trim().equals("")) {
                String[] tokens = line.split("\\s*,\\s*");
                Product product = new Product(Integer.parseInt(tokens[0]), tokens[1],
                        new BigDecimal(tokens[2]));
                returnSet.add(product);
                line = reader.readLine();
            }
        }
        return returnSet;
    }
}

CustomerCsvReader.java

public class CustomerCsvReader {
 
    Set<Customer> getAll(File file) throws IOException {
        Set<Customer> returnSet = new HashSet<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(file))){
            String line = reader.readLine();
            while (line != null && !line.trim().equals("")) {
                String[] tokens = line.split("\\s*,\\s*");
                Customer customer = new Customer(Integer.parseInt(tokens[0]), tokens[1],
                        tokens[2], tokens[3]);
                returnSet.add(customer);
                line = reader.readLine();
            }
        }
        return returnSet;
    }
}

For this example, there are only two entities, but a real system might have dozens of entities, so that’s a lot of error-prone duplicate code. You might find a similar situation with DAOs, where the select, insert, update, and delete operations of each DAO would do the same thing, only work with different entities and tables. Let’s start refactoring this troublesome code. According to one of the design principles found in the first part of the GoF Design Patterns book, we should “Encapsulate the concept that varies.” Between ProductCsvReader and CustomerCsvReader, what varies is the highlighted code. So our goal is to encapsulate what varies into separate classes, while moving what stays the same into a single class. Let’s start editing just one class first, ProductCsvReader. We use Extract Method to extract the line into its own method:

ProductCsvReader.java after Extract Method

public class ProductCsvReader {
 
    Set<Product> getAll(File file) throws IOException {
        Set<Product> returnSet = new HashSet<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(file))){
            String line = reader.readLine();
            while (line != null && !line.trim().equals("")) {
                String[] tokens = line.split("\\s*,\\s*");
                Product product = unmarshall(tokens);
                returnSet.add(product);
                line = reader.readLine();
            }
        }
        return returnSet;
    }

    Product unmarshall(String[] tokens) {
        Product product = new Product(Integer.parseInt(tokens[0]), tokens[1], 
                new BigDecimal(tokens[2]));
        return product;
    }
}

Now that we have separated what varies with what stays the same, we will create a parent class that will hold the code that stays the same for both classes. Let’s call this parent class AbstractCsvReader. Let’s make it abstract since there’s no reason for the class to be instantiated on its own. We’ll then use the Pull Up Method refactoring to move the method that stays the same to this parent class.

AbstractCsvReader.java

abstract class AbstractCsvReader {

    Set<Product> getAll(File file) throws IOException {
        Set<Product> returnSet = new HashSet<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(file))){
            String line = reader.readLine();
            while (line != null && !line.trim().equals("")) {
                String[] tokens = line.split("\\s*,\\s*");
                Product product = unmarshall(tokens);
                returnSet.add(product);
                line = reader.readLine();
            }
        }
        return returnSet;
    }
}

ProductCsvReader.java after Pull Up Method

public class ProductCsvReader extends AbstractCsvReader {

    Product unmarshall(String[] tokens) {
       Product product = new Product(Integer.parseInt(tokens[0]), tokens[1], 
                new BigDecimal(tokens[2]));
        return product;
    }
}

This class won’t compile since it calls an “unmarshall” method that’s found in the subclass, so we need to create an abstract method called unmarshall.

AbstractCsvReader.java with abstract unmarshall method

abstract class AbstractCsvReader {

    Set<Product> getAll(File file) throws IOException {
        Set<Product> returnSet = new HashSet<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(file))){
            String line = reader.readLine();
            while (line != null && !line.trim().equals("")) {
                String[] tokens = line.split("\\s*,\\s*");
                Product product = unmarshall(tokens);
                returnSet.add(product);
                line = reader.readLine();
            }
        }
        return returnSet;
    }

    abstract Product unmarshall(String[] tokens);
}

Now at this point, AbstractCsvReader will make a great parent for ProductCsvReader, but not for CustomerCsvReader. CustomerCsvReader will not compile if you extend it from AbstractCsvReader. To fix this, we use Generics.

AbstractCsvReader.java with Generics

abstract class AbstractCsvReader<T> {

    Set<T> getAll(File file) throws IOException {
        Set<T> returnSet = new HashSet<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(file))){
            String line = reader.readLine();
            while (line != null && !line.trim().equals("")) {
                String[] tokens = line.split("\\s*,\\s*");
                T element = unmarshall(tokens);
                returnSet.add(product);
                line = reader.readLine();
            }
        }
        return returnSet;
    }

    abstract T unmarshall(String[] tokens);
}

ProductCsvReader.java with Generics

public class ProductCsvReader extends AbstractCsvReader<Product> {

    @Override
    Product unmarshall(String[] tokens) {
       Product product = new Product(Integer.parseInt(tokens[0]), tokens[1], 
                new BigDecimal(tokens[2]));
        return product;
    }
}

CustomerCsvReader.java with Generics

public class CustomerCsvReader extends AbstractCsvReader<Customer> {

    @Override
    Customer unmarshall(String[] tokens) {
        Customer customer = new Customer(Integer.parseInt(tokens[0]), tokens[1], 
                tokens[2], tokens[3]);
        return customer;
    }
}

And that’s it! No more duplicate code! The method in the parent class is the “template”, which holds the code that stays the same. The things that change are left as abstract methods, which are implemented in the child classes. Remember that when you refactor, you should always have automated Unit Tests to make sure you don’t break your code. I used JUnit for mine. You can find the code I’ve posted here, as well as a few other Design Patterns examples, at this Github repository. Before I go, I’d like to leave a quick note on the disadvantage of the Template Method. The Template Method relies on inheritance, which suffers from the the Fragile Base Class Problem. In a nutshell, the Fragile Base Class Problem describes how changes in base classes get inherited by subclasses, often causing undesired effects. In fact, one of the underlying design principles found at the beginning of the GoF book is, “favor composition over inheritance”, and many of the other design patterns show how to avoid code duplication, complexity or other error-prone code with less dependence on inheritance. Please give me feedback so I can continue to improve my articles.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

6 Responses to "Template Method Pattern Example Using Java Generics"

  1. PiotrL says:

    Why then promote pattern that IS bad?

  2. Marcelo says:

    Great article, Calen! Well done!

    Many developers and notorious experts say we should avoid using inheritance (as you mentioned as well), but… on the way you’ve developed your code, this “bad principle” worked fine, is there any other solution without using inheritance? I have the same issue with in my own code design and I didn’t find a better one.

    Thanks!

  3. Valery says:

    Marcelo,

    Obviuosly, the most common alternative to the inheritance is a delegation.
    1. Create (single abstract method) interface for unmarshalling:
    interface Unmarshaller {
    T unmarshall(String[] tokens)
    }
    2. Refactor AbstractCsvReader to GenericCsvReader:
    public class GenericCsvReader {
    final private Unmarshaller unmarshaller;
    public GenericCsvReader(Unmarshaller unmarshaller) {
    this.unmarshaller = unmarshaller;
    }

    public Set getAll(File file) throws IOException {

    //line 09 should be delegate call:
    T element = unmarshaller.unmarshall(tokens);

    }
    }
    3. Create separate implementations of the Unmarshaller interface for Product and Customer; if you are using Java 8 you may use lambda expressions for this.

    As a result, you have refactored Template Method pattern (that relies on inheritance) to Strategy patten (that uses delegation).

  4. Thank you, Marcelo, Valery and PiotrL, for commenting on my post! Your feedback helps me improve my article.

    Inheritance per se is not bad, but just like any tool you need to be aware of the dangers, and then weigh the tradeoffs. Inheritance is dangerous when there is a significant probability that the parent class might be modified in the future, causing undesired or breaking changes in the child classes. It is also dangerous when the child classes inherit members that they don’t use, which could lead to error prone code. Read up on Open-Closed Principle and Refused Bequest Code Smell.

    If the parent class is designed to not change, then the Template Method offers a very simple solution. If we used the Strategy pattern as Valery suggested, the client code would need to deal with two classes – GenericCsvReader and the appropriate Unmarshaller, instead of just one class – the appropriate CsvReader.

    So basically, have a complete set of tools in your toolbox, so that you use the right tool for the right situation. Keep reading up on design patterns so you pick the right one for the job.

    This discussion encourages me to write more about design patterns. Thank you! I hope you will engage me in comments in my future posts.

  5. Bryan Basham says:

    Excellent article. Nice simple example. Great use of refactoring; as well. I’m going to recommend your article to my RIT students.

Leave a Reply


+ 7 = eight



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close