views:

134

answers:

4

Hello,

I have several classes which work as wrappers over some heavy libs, such as parsers, taggers, and other resources. All these libs have one thing in common: they have a load/init method which takes a serialized model (a large file) as input, and return a class that can be used to do concrete things such as parsing text.

To illustrate this situation, lets say I have a ConcreteParser lib to do parsing, for which I have created the following class to wrap it:

public class ParserWrapper {
    private static final ConcreteParser parser = null;

    private static void init(String modelFileName) {
        if (parser == null)
            parser = ConcreteParser.load(modelFileName);
    }

    public static Result parse(input) {
        if (parser == null) throw new RuntimeException(...);
        return parser.parse(input);
    }

}

As you can see, this requires the client to first invoke init before parse, which is definitely a no-no. I have also tried a solution based on the Singleton pattern, but I still think there's a better way to do what I want.

The ConcreteParser is static because every model takes a relatively long time to load, and consume lots of memory. Therefore, I need them to be shared among every class that uses it.

So, my question is: is there another (more elegant) way to do what I want? How can I improve this code? I thought of creating a class ResourceManager, that had several methods like "createParser" and "createTagger", in order to have a single point to load resources. This class would also check if each resource had already been instantiated, et cetera. Any thoughs on these?

Regards.

A: 

Why don't you make the ResourceManager static, and then call all of your create methods before initializing your wrappers? This avoids the singleton pattern and probably more closely matches what you'd like to achieve.

AlbertoPL
+3  A: 

I would create a Factory like class to create Parser and Tager etc. and have this class cache results for certain filenames so that the first call to getParser(filename) will cause a parser to be created and all the next will return the cached parser. This is similar to the Singleton but if you have a common interface for the factorys allows to create a factory that returns mocks instead of creating the whole parser.

This way you only have to ensure that somewhere in the program you create a factory for all the parsers and taggers and have a good way to obtain this objects from within your program.

Janusz
+1 to Factory, as the details can be easily changed later without breaking anything
Mario Ortegón
A: 

Does the file ever change?

If not: Load it as a resource within a static intialiser.

If so: "Parameterise from Above" Pass in objects through constructors, all the way down.

Tom Hawtin - tackline
+1  A: 

It seems that parser is immutable. (if parser is not null, init does nothing) so why bother making it static? Make init the constructor and parser a member of ParswerWrapper. Create another class (or use System.setProperty() as an ugly hack) that is a Singleton whose only job is to hold onto references. If you know ahead of time which files you need you can even use a Enum!

#1

/*package*/ class ParserWrapper implements ParserWrapperInterface{ 
  private ConcreteParser p;
  public ParserWrapper(String filename) {
    p = ConcreteParser.load(p);
  }

  public Result parse(InputStream in) {...}
}

public Enum ParserManager {
  instance;

  private Map<String, ParserWrapper> map = new HashMap<...>()

  public get(String filename) {
    if(!map.containesKey(filename)) {
      synchronized(ParserManager.class) {
        if(!map.containesKey(filename)) {
          map.put(filename, new ParserWrapper(filename));
        }
      }
    }
    return map.get(filename);
  }
}

#2

public Enum Parsers {
ModelFoo("foo.mdl"), ModelBar("bar.mdl");

private ConcreteParser p;

public Parser(String fname) {
p = ConcreteParser.load(fname);
}

public Result parse(InputStream in) {...}
}

Using Enum guarantees that there is ever only one instance of each Enum value. Period. The JVM is suppossed to ensure that they can't be cloned, deserialized, split among classloaders, etc. Enum means single instance.

KitsuneYMG