More on generics and Inversion of Control

Published 06 April 08 09:29 PM | andersnoras 

My last post on Java generics and the Repository pattern got a bit of interest. Judging from the feedback I got, I feel the need to elaborate on the Inversion of Control part of this. As some readers pointed out, Google’s Guice container employes super type tokens to support generics to some extent. The IoC example I gave in my previous post can be implemented with Guice by binding a TypeLiteral of the interface to a concrete implementation of that interface.

public class RepositoriesModule implements Module {
   public void configure(Binder binder) {
       binder.bind(new TypeLiteral<Repository<Product>>() {}).to(ProductRepository.class);
   }
}

You can resolve an implementation of a Repository by asking the injector for an instance of the same TypeLiteral that instance is bound to.

Injector injector = Guice.createInjector(new RepositoriesModule());
Repository<Product> repository = injector.getInstance(Key.get(new TypeLiteral<Repository<Product>>() {}));

However, this does provide a solution similar to how we can inject generic repositories using something like the Windsor container for .NET. With Windsor, you can bind a generic interface, say IRepository, to a generic implementation of that interface such as ConcreteRepository : IRepository. This allows us to resolve a useable repository instance like this:

WindsorContainer container = new WindsorContainer();
IRepository<Product> repository = container.Resolve<IRepository<Product>>();

One thing to note here is that there is no need to have concrete implementations for every domain object (Product, Order, Customer and similar), a generic implementation of the ConcreteRepository is sufficient because .NET has bona fide generics implemented in the Common Language Runtime (CLR) and the CLR will create a new bound type of the generic class at runtime. This is even the case if we use reflection to create the instance.

The Guice container allows us to specify providers to which the creation of a dependency will be delegated. However, this provider is bound to the type arguments for the TypeLiteral and wild card type arguments cannot be used because the compiler is unable to infer the runtime type of such an argument. As a result we would need to bind all the TypeLiterals to different concrete implementations or different providers to support this pattern with Guice. This is a show stopper because it takes away the pattern’s flexibility. To exemplify the concept, I changed my naïve IoC container from my previous post to support this pattern using Guice’s TypeLiterals. Here is an example of binding the Repository interface to a provider which will create the typed instances via the RepositoryFactory shown in my previous post. (If you haven’t read that post, now is a good time.)

@Test
public void canRegisterAnOpenGenericInterfaceAndResolveItsImplementationViaABoundInterface()
{
    // Register the open generic interface and bind it to a provider.
    IoC.register(new TypeLiteral<Repository>() {},new Provider<Repository>() {
        @SuppressWarnings({"unchecked"})
        public Repository get(TypeLiteral<? extends Repository> type) {
            ParameterizedType pt = (ParameterizedType) type.getType();
            return RepositoryFactory.create((Class<?>) pt.getActualTypeArguments()[0]);
        }
    });
    // Resolve an instance of a bound interface.
    Repository<Order> repository = IoC.resolve(new TypeLiteral<Repository<Order>>() {});
    Assert.assertTrue(repository instanceof AbstractRepository);
    Assert.assertSame(Order.class,repository.getEntityClass());
}

In this example we first register the open generic interface, akin to IRepository<> in .NET, to a provider for that interface. The provider implementation has to perform some unchecked operations to allow this to work, but this is not really something to worry about. When we resolve the instance, we pass a fully bound TypeLiteral to the container. The provider is passed this TypeLiteral as an argument, and the type argument is extracted from it. In this example this will be a TypeLiteral>. Since the provider is bound to the open generic type Repository, the extracted type argument will be Order. This provides us with enough type information to create a strongly type instance of the AbstractRepository via the RepositoryFactory class.

We also need to be able to distinguish between generic implementations and concrete ones. So lets bring in the ProductRepository class from my previous post.

@Test
public void boundServicesHavePrecedenceOverOpenGenericServices()
{
    IoC.register(new TypeLiteral<Repository>() {},new Provider<Repository>() {
        @SuppressWarnings({"unchecked"})
        public Repository get(TypeLiteral<? extends Repository> type) {
            ParameterizedType pt = (ParameterizedType) type.getType();
            return RepositoryFactory.create((Class<?>) pt.getActualTypeArguments()[0]);
        }
    });
    IoC.register(new TypeLiteral<Repository<Product>>() {},ProductRepository.class);

    Repository<Product> repository = IoC.resolve(new TypeLiteral<Repository<Product>>() {});
    Assert.assertTrue(repository instanceof ProductRepository);
    Assert.assertSame(Product.class, repository.getEntityClass());
}

This is basically the same test case over, but with one important difference. In this example two services are registered in the container. First the provider for the open generic Repository interface is registered, and then the concrete implementation for the bound Repository is registered. When we resolve the service for TypeLiteral> we expect to get an instance of ProductRepository back, rather than a runtime generated instance of the AbstractRepository.

Below is the entire implementation of the “container” used in these tests.

public class IoC {
    static Map<TypeLiteral, Provider> registry = new HashMap<TypeLiteral, Provider>();
    public static <T> void register(TypeLiteral<T> service, Provider<T> provider) {
        registry.put(service, provider);
    }
    public static <T> void register(TypeLiteral<T> service, Class<? extends T> impl) {
        registry.put(service, new ConcreteProvider(impl));
    }
    @SuppressWarnings({"unchecked"})
    public static <T> T resolve(TypeLiteral<T> type) {
        if (registry.containsKey(type)) {
            return (T) registry.get(type).get(type);
        } else {
            Type c = type.getRawType();
            while (c != Object.class) {
                ManualTypeLiteral<T> manualTypeLiteral = new ManualTypeLiteral<T>(c);
                if (registry.containsKey(manualTypeLiteral)) {
                    return (T) registry.get(manualTypeLiteral).get(type);
                }
                c = c.getClass().getSuperclass();
            }
            throw new RuntimeException("Not found");
        }
    }
    private static class ManualTypeLiteral<T> extends TypeLiteral<T> {
        private ManualTypeLiteral(Type type) {
            super(type);
        }
    }
    private static class ConcreteProvider<T> implements Provider {
        private final Class<? extends T> impl;
        public ConcreteProvider(Class<? extends T> impl) {
            this.impl = impl;
        }
        public T get(TypeLiteral type) {
            try {
                return impl.getConstructor().newInstance();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
}

The implementation of the container has been simplified to make the example more clear, and is by no means a real container ready for production use. However, I think it should be fairly easy to add this kind of behavior to Guice, and I also believe that it should be well worth while because this kind of flexiblity opens the doors for a whole lot of usage patterns that we are just starting to understand the power of within the .NET community.

Filed under: , ,

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Martin said on April 14, 2008 9:56 AM:

Anders,

I just skimmed through your explanations above for a current lack of time, but I find your ideas really, really interesting. I will definitely have to spare some time to get all this right. Even though I commented about Guice's TypeLiteral in your last blog entry, I have got admittedly no real experience with Guice except for taking a look at the source of said TypeLiteral class and skimming throught the user guide. Anyway, thanks for these two interesting articles! I might come back with a question or two if you don't mind ;)

Martin

# andersnoras said on April 15, 2008 1:26 AM:

@Martin;

Thank you. There are probably many issues with my spikes that I've overlooked, but I have a feeling that I'm on to something here. I've used similar techniques with .NET for some time and they really change the way you can build applications. Questions, ideas or other comments are more than welcome.

# Confluence: Enterprise Architecture said on August 20, 2008 1:01 AM:

http://wiki.community.objectware....

# Confluence: Enterprise Architecture said on August 20, 2008 9:46 AM:

Intro The Emerging Technologies the future of enterprise POJOS is the first in a series of open seminars/workshops. The idea is to facilitate discussions between thought leaders and peers on important aspects of the software development....

# Confluence: Enterprise Design and Architecture said on September 13, 2008 6:16 AM:

Preparations and recommended reading before the Geek Cruise Practical stuff Bring your own laptop Check out the Qi4j codebase

# Confluence: Enterprise Design and Architecture said on September 13, 2008 6:21 AM:

Recommended reading Qi4j I picked Qi4j because it allows me (or it will allow me once I work out the kinks in integrating it with the specific things I want to use like Struts) to focus on the domain and providing a rich environment for the users....

Leave a Comment

(required) 
(optional)
(required) 
Enter the code you see below