简体   繁体   English

一种实现加载大量资源的共享类的绝佳方法(使用Java)

[英]An elegant way to implement shared classes that load heavy resources (in Java)

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. 所有这些库都有一个共同点:它们都有一个load / init方法,该方法将序列化模型(一个大文件)作为输入,并返回一个可用于执行具体工作(例如解析文本)的类。

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: 为了说明这种情况,可以说我有一个ConcreteParser库来进行解析,为此我创建了以下类来包装它:

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. 如您所见,这要求客户端在解析之前首先调用init,这绝对是不可以的。 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. 我也尝试过基于Singleton模式的解决方案,但我仍然认为有一种更好的方法可以完成我想要的事情。

The ConcreteParser is static because every model takes a relatively long time to load, and consume lots of memory. ConcreteParser是静态的,因为每个模型都需要相对较长的时间来加载,并占用大量内存。 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. 我想创建一个类ResourceManager,它具有诸如“ createParser”和“ createTagger”之类的几种方法,以便于单点加载资源。 This class would also check if each resource had already been instantiated, et cetera. 该类还将检查每个资源是否已经实例化,等等。 Any thoughs on these? 这些有什么吗?

Regards. 问候。

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. 我将创建一个类似于Factory的类来创建解析器和Tager等,并让此类为某些文件名缓存结果,以便对getParser(filename)的第一个调用将导致创建解析器,而所有下一个都将返回缓存的解析器。 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. 这类似于Singleton,但是如果您具有工厂的通用接口,则可以创建一个返回模拟的工厂,而不是创建整个解析器。

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. 这样,您只需确保在程序中的某个地方为所有解析器和标记器创建了一个工厂,并拥有从程序中获取此对象的好方法。

It seems that parser is immutable. 解析器似乎是不可变的。 (if parser is not null, init does nothing) so why bother making it static? (如果解析器不为null,则init不执行任何操作)为什么还要麻烦使其变为静态? Make init the constructor and parser a member of ParswerWrapper. 使初始化构造函数和解析器成为ParswerWrapper的成员。 Create another class (or use System.setProperty() as an ugly hack) that is a Singleton whose only job is to hold onto references. 创建另一个类(或将System.setProperty()用作丑陋的hack),该类是Singleton,其唯一的工作就是保留引用。 If you know ahead of time which files you need you can even use a Enum! 如果您提前知道需要哪些文件,您甚至可以使用Enum!

#1 #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 #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. 使用Enum保证每个Enum值永远只有一个实例。 Period. 期。 The JVM is suppossed to ensure that they can't be cloned, deserialized, split among classloaders, etc. Enum means single instance. 假设JVM是为了确保它们不能被克隆,反序列化,在类加载器之间拆分等。Enum表示单个实例。

Why don't you make the ResourceManager static, and then call all of your create methods before initializing your wrappers? 为什么不将ResourceManager设为静态,然后在初始化包装器之前调用所有create方法? This avoids the singleton pattern and probably more closely matches what you'd like to achieve. 这避免了单例模式,并且可能更紧密地匹配您想要实现的目标。

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. 如果是这样的话:“从上方进行参数设置”将对象一直传递到构造函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM