8

I'm developing computer vision application and I will need Classifier class. This class will be immutable per run of application and it loads the trained data from disk on initialization. I want to ensure that whole program will have access to same trained data and I want to block reloading the from disk once they are loaded.

What I was considering was to use either static class or singleton. I'm not sure how to load data to static class, because the path to data file is not know at compile time - it will be program argument. So I was thinking of Singleton pattern, but there I don't know how to initialize it dynamically.

My idea was to use following:

class Singleton {
    private static Singleton instance;
    private Singleton() { ... }
    private static SomeDataObject data;

    public static Singleton getInstance() {
        if(instance == null)
            instance = new Singleton();

            return instance;
    }

    public static init(string dataPath){
        if(data == null)
             loadDataFromFile(dataPath)
    }
}

This would not work, because I have no control which method will be called first.

I know the proper way would be to create the instance with data at the begining and pass it to all classes and methods which need it, but that's not really general solution. I can keep track of all calls to Classifier in my own code, but if I would make the code as API, this would be a problem.

In short how to initialize singleton at runtime?

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
jnovacho
  • 2,825
  • 6
  • 27
  • 44
  • How is the program argument `dataPath` programmatically accessible? – Matt Ball Feb 19 '13 at 17:02
  • You don't need ***both*** a static singleton instance ***and*** data object. Also, your current singleton implementation is not thread safe. – Perception Feb 19 '13 at 17:05
  • @MattBall it could be either from GUI, or command line argument – jnovacho Feb 19 '13 at 17:10
  • First of all, you need to be sure you need to use Singleton pattern. Singleton should be used when you need to > Ensure that only one instance of a class is created. < AND to > Provide a global point of access to the object. < Source: http://www.oodesign.com/singleton-pattern.html It seems that you do not need a Singleton. Global variables are bad, Singletons too. – bancer Feb 19 '13 at 17:23
  • @bancer I need single instance of SomeDataObject accessible and this instance should accessible by multiple other types of Classifiers, not necessary sharing some parent class. – jnovacho Feb 19 '13 at 17:30
  • If you need to ensure that only one instance is allowed set a static private attribute as a flag. "Applicability & Examples" section describes very few cases when you need to use Singleton. Pass Classifier instance to other objects as a parameter. – bancer Feb 19 '13 at 17:40
  • http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons – bancer Feb 19 '13 at 18:02

2 Answers2

8

I don't think (exactly) what you want to do would work.

The below would work:

public static void main(String[] args)
{
  Singleton.init("somepath");
  ...
  Singleton.getInstance().doingStuff();
  ...
}

A better implementation may be: (which would cause a NullPointerException if you try to use it without calling init first) (not really Singleton any more though)

private static Singleton instance;
private SomeDataObject data;

private Singleton(String path) { loadDataFromFile(path); ... }

public static Singleton getInstance() {
   return instance;
}

public static void init(String dataPath){
   instance = new Singleton(dataPath);
}

Then there's: (possible bad coding practice aside)

class Main
{
  public static void main(String[] args)
  {
    Singleton.currentPath = "somepath";
    ...
  }
}

class Singleton
{
  public static String currentPath = null;
  private static Singleton instance;
  private SomeDataObject data;

  private Singleton(String path) { loadDataFromFile(path); ... }

  public static Singleton getInstance() {
     if(instance == null && currentPath != null)
        instance = new Singleton(currentPath);
     return instance;
  }
}

which I suppose doesn't really solve much.

Bernhard Barker
  • 54,589
  • 14
  • 104
  • 138
  • But I can also do Singleton.getInstance().doStuff() and that would be error, because no initialization was done. As I stated in my post, in my code I can keep track of this, but in general this solves nothing. – jnovacho Feb 19 '13 at 17:08
  • @jnovacho You can have the constructor read a static variable stored somewhere else, but I suppose this is basically the same problem. I don't think you can do better than what I provided in the answer (unless you want to pass the path on each call to `getInstance`). – Bernhard Barker Feb 19 '13 at 17:12
  • for me first one worked with public static void init() – Ebru Güngör Aug 15 '17 at 15:07
0

I use something that is "more" threadsafe than the current winning solution with almost no synchronized used.

import java.util.function.Supplier;

public class InitOnce {

/**
 * Marked as final to prevent JIT reordering
 */
private final Supplier<String> theArgs;

private InitOnce(Supplier<String> supplier) {
    super();
    this.theArgs = supplier;
}

/**
 * Uses the arguments to do something
 * 
 * @return
 */
public String doSomething() {
    return "Something : " + theArgs.get();
}

/**
 * Initializes all the things
 * 
 * @param someArgs
 */
public static synchronized void init(final Supplier<String> someArgs) {
    class InitOnceFactory implements Supplier<InitOnce> {
        private final InitOnce initOnceInstance = new InitOnce(someArgs);

        @Override
        public InitOnce get() {
            return initOnceInstance;
        }
    }

    if (!InitOnceFactory.class.isInstance(instance)) {
        instance = new InitOnceFactory();
    } else {
        throw new IllegalStateException("Already Initialized");
    }
}

private static Supplier<InitOnce> instance = new InitOnceHolder();

/**
 * Temp Placeholder supplier
 * 
 */
private static final class InitOnceHolder implements Supplier<InitOnce> {
    @Override
    public synchronized InitOnce get() {

        if (InitOnceHolder.class.isInstance(instance))
            throw new IllegalStateException("Not Initialized");

        return instance.get();
    }

}

/**
 * Returns the instance
 * 
 * @return
 */
public static final InitOnce getInstance() {
    return instance.get();
}
}
BeCodeMonkey
  • 91
  • 1
  • 6