Jeff's Blog

Musings about software development, Java, OO, agile, life, whatever.


Wednesday, November 02, 2005 
Database TDD Part 15: Eliminating Duplication With Generics

The UserAccess and CustomerAccess classes each contain the method save and find. Here's the code from one of them:

   public void save(Persistable persistable) {
      new Persister(this).save(persistable);
   }

   public Customer find(String idKey) {
      return (Customer)new Persister(this).find(idKey);
   }
In save, the code looks exactly the same. The subtle difference is that the method contains a reference to this. I could easily push up the save method to a common superclass, named perhaps DataAccess. That superclass would then need to implement the PersistableMetadata interface. There's a minor nit: the name would bely the code contained within--the save method has nothing to do with metadata. I'm still searching for more appropriate names.

The find method is even more difficult, since it contains a cast to the specific type being retrieved. If I move that method directly to the superclass, its return type must change to Object. That would require clients to cast, something that's unacceptable. I could also provide a subclass implementation with a variant return type, something else J2SE 5.0 supports, but then I'd have to provide a nuisance subclass method that delegated to the superclass. So much for completely eliminating duplication.

If you're not working with J2SE 5.0, you'll have to make the best of this. I'd probably not worry about it much. If you are using J2SE 5.0, you can use generics to eliminate the duplication. This requires a bunch of trivial changes to the code.

UserAccess

package domain;

import java.util.*;

public class UserAccess extends DataAccess<User> {
   public User create(List<String> row) {
      return new User(row.get(0), row.get(1));
   }

   public String getTable() {
      return "userdata";
   }

   public String[] getColumns() {
      return new String[] { User.NAME, User.PASSWORD };
   }

   public String getKeyColumn() {
      return User.NAME;
   }
}
The UserAccess class now extends the parameterized type DataAccess, which will contain the save and find methods. The extends in UserAccess binds DataAccess to the User type. Note that the create method directly returns the User type.

DataAccess<T>

package domain;

abstract public class DataAccess<T> implements PersistableMetadata<T> {
   public void save(Persistable persistable) {
      new Persister<T>(this).save(persistable);
   }

   protected T find(String idKey) {
      return new Persister<T>(this).find(idKey);
   }
}
The DataAccess type is now parameterized. To avoid warnings, it's also necessary to parameterize the Persister type, in addition to PersistableMetadata:

PersistableMetadata<T>

package domain;

import java.util.*;

public interface PersistableMetadata<T> {
   String getTable();
   String[] getColumns();
   String getKeyColumn();
   T create(List<String> row);
}

Persister<T>

package domain;

import java.util.*;

import persistence.*;

public class Persister<T> {
   private PersistableMetadata<T> metadata;
   private JdbcAccess access;

   public Persister(PersistableMetadata<T> metadata) {
      this(metadata, new JdbcAccess());
   }

   public Persister(PersistableMetadata<T> metadata, JdbcAccess access) {
      this.metadata = metadata;
      this.access = access;
   }

   public void save(Persistable persistable) {
      String[] columns = metadata.getColumns();
      String[] fields = new String[columns.length];
      for (int i = 0; i < columns.length; i++)
         fields[i] = persistable.get(columns[i]);
      String sql = new SqlGenerator().createInsert(metadata.getTable(), columns, fields);
      access.execute(sql);
   }

   public T find(String key) {
      String sql = new SqlGenerator().createFindByKey(metadata.getTable(), metadata.getColumns(),
            metadata.getKeyColumn(), key);
      List<String> row = access.executeQuery(sql);
      return metadata.create(row);
   }
}
The PersisterTest class also must change to eliminate warnings. I just bound everything to Object for the simplest solution.

I like the result so far. UserAccess and CustomerAccess are about as duplication-free as they're going to get. Here's the CustomerAccess class:

CustomerAccess

package domain;

import java.util.*;

public class CustomerAccess extends DataAccess<Customer> {
   public String getTable() {
      return "cust";
   }

   public String[] getColumns() {
      return new String[] { Customer.ID, Customer.NAME };
   }

   public String getKeyColumn() {
      return Customer.ID;
   }

   public Customer create(List<String> row) {
      return new Customer(row.get(0), row.get(1));
   }
}


Comments: Post a Comment

Links to this post:

Create a Link



<< Home

RSS Feed (XML)

Archives

February 2004   March 2004   May 2004   September 2004   October 2004   January 2005   February 2005   September 2005   October 2005   November 2005   December 2005   January 2006   February 2006   March 2006   June 2006   August 2006   January 2007   February 2007   March 2007   April 2007   September 2007   October 2007   November 2007   December 2007   January 2008  

This page is powered by Blogger. Isn't yours?