实现加载大量资源的共享类的优雅方法(在Java中)

时间:2009-07-18 22:52:15

标签: java design-patterns oop resources

我有几个类可以作为一些重型库的包装器,例如解析器,标记器和其他资源。所有这些库都有一个共同点:它们有一个load / init方法,它将序列化模型(一个大文件)作为输入,并返回一个可用于执行具体事务的类,如解析文本。

为了说明这种情况,假设我有一个ConcreteParser lib来进行解析,为此我创建了以下类来包装它:

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);
    }

}

正如您所看到的,这需要客户端在解析之前首先调用init,这绝对是禁止的。我也尝试过基于Singleton模式的解决方案,但我仍然认为有更好的方法来做我想要的。

ConcreteParser是静态的,因为每个模型都需要相对较长的时间来加载,并占用大量内存。因此,我需要在使用它的每个类之间共享它们。

所以,我的问题是:是否有其他(更优雅)的方式来做我想要的?我该如何改进这段代码?我想创建一个类ResourceManager,它有几个方法,如“createParser”和“createTagger”,以便有一个点来加载资源。该类还将检查每个资源是否已经实例化,等等。这些都有吗?

问候。

4 个答案:

答案 0 :(得分:3)

我会创建类似Factory的类来创建Parser和Tager等,并为某些文件名设置此类缓存结果,以便第一次调用getParser(filename)将导致创建解析器,并且所有下一个将返回缓存解析器。 这与Singleton类似,但是如果你有一个通用的factorys接口允许创建一个返回模拟的工厂,而不是创建整个解析器。

这样,您只需要确保程序中的某个位置为所有解析器和标记器创建工厂,并且可以从程序中获取此对象。

答案 1 :(得分:1)

似乎解析器是不可变的。 (如果解析器不为null,init什么都不做)那么为什么还要把它变成静态呢?使init成为构造函数并解析ParswerWrapper的成员。创建另一个类(或使用System.setProperty()作为一个丑陋的黑客),这是一个Singleton,其唯一的工作是保留引用。如果您提前知道需要哪些文件,甚至可以使用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) {...}
}

使用Enum保证每个Enum值只有一个实例。期。 JVM被假定为不能克隆,反序列化,在类加载器之间拆分等。枚举意味着单个实例。

答案 2 :(得分:0)

为什么不将ResourceManager设置为静态,然后在初始化包装器之前调用所有的create方法?这避免了单例模式,可能更符合你想要达到的目标。

答案 3 :(得分:0)

文件是否会改变?

如果不是:将其作为静态intialiser中的资源加载。

如果是这样的话:“从上面参数化”通过构造函数传递对象,一直向下。