1
\$\begingroup\$

Given the need to share several big models throughout an application (web application, the models need to persist between requests), I would like to create a unified cache handler to keep them in memory. Creating the models from disk is expensive. There's an abstract model container which handles the cache management. The users of the library need only to extend that abstract handler and specify how to create the model.

Type erasure stood in the way of first attempt, since class Foo<T> { static T foo; } is not allowed. There's no way to distinguish Foo<Bar> from Foo<Baz> at runtime, so you can't have static access to a generic class member. The resulting code is somewhat awkward, since users need to specify which type of model they want. The models are further classified by language, which is handled by an enum type Language.

abstract class AbstractModel<T> {

    private static final Logger log = Logger.getLogger(AbstractModel.class);
    protected static Map<Language, Map<Class<?>, Object>> cache =
         new HashMap<>();

    public final T request(Class<T> c, Language language) throws NoModelException {
        final Map<Class<?>, Object> langCache = AbstractModel.cache.getOrDefault(
                language,
                this.makeCache(c, language)
        );

        final Object o = langCache.getOrDefault(c, this.create(language));
        if (c.isInstance(o)) {
            return c.cast(o);
        } else {
            throw new NoModelException(
                    "Wrong model type '" + o.getClass() + 
                    "', expected '" + c.getName() + "'."
            );
        }
    }

    private Map<Class<?>, Object> makeCache(Class<T> c, Language language)
        throws NoModelException {
        final HashMap<Class<?>, Object> newCache = new HashMap<>();
        final T newModel = this.create(language);
        newCache.put(c, newModel);
        return newCache;
    }

    protected abstract T create(Language language) 
        throws NoModelException;
}

The provider now extends abstract model:

public class HmmModel extends AbstractModel<HmmDecoder> {
    @Override
    protected HmmDecoder create(Language language) throws NoModelException {
        final HiddenMarkovModel hmm = new HiddenMarkovModel(fromResource);
        return new HmmDecoder(hmm);
    }
}

The user can request a new model thus:

HmmDecoder tagger = new HmmModel().request(HmmDecoder.class, ENGLISH);

You have to instantiate the inheriting classes, because request can't be static for the same reason fields can't be static (it references the generic variable T on its return type.)

I'm not sure this is the most elegant or efficient solution — but it doesn't have to be super-efficent. There's a about two dozen models, no big deal. I'm more aiming for ease of use and a sound implementation. I'm more concerned about storing the models as Object, and the resulting casts (though I think they should be safe.) The nested maps and retrieval from them are also somewhat messy.

\$\endgroup\$
  • \$\begingroup\$ I don't understand what you actually want. "users need to specify which type of model they want". If that's not what you want, who should define which model to create/use in each specific request? If the models don't have a common base class/interface, the user must implement for the specific model classes. \$\endgroup\$ – JimmyB Sep 9 '16 at 18:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.