javasingletonmultiton

Change a Singleton with static properties into a Multiton


I have the following Singleton class

public class TestFileEngine {
    private static int counter = 0;
    private static List<GeneratedFile> generatedFileList;
    private static Optional<TestFileEngine> engine = Optional.empty();

    private TestFileEngine() {
        generatedFileList = Collections.synchronizedList(new ArrayList<>());
    }

    public static synchronized TestFileEngine getInstance() {
        if (!engine.isPresent()) {
            engine = Optional.of(new TestFileEngine());
        }

        return ruEngine.get();
    }

    public synchronized void generateFile() {
        counter++;
        String timestamp = LocalDateTime.now().toString();
        String content = "";//insert random content generation
        GeneratedFile gf = new GeneratedFile(counter + ".txt", timestamp, content);
        generatedFileList.add(gf);
        System.out.println("Generated file " + counter + ".txt");
    }

    public GeneratedFile findFileByName(String filename) {
        for (GeneratedFile file : generatedFileList){
           if(file.getFileName().equals(filename)){
               return file;
           }
        }
        return null;
    }
}

Now I want to have two separate engines (and possibly more in the future), for tracking purpose and I stumbled upon the multiton pattern, still using the lazy implementation. So I will change the following:

//Getting rid of Optional, as it can get funky with Maps
private static final Map<String, TestFileEngine> _engines = new HashMap<String, TestFileEngine>();

public static synchronized TestFileEngine getInstance(String key) {
    if (_engines.get(key) == null) {
        _engines.put(key, new TestFileEngine());
        System.out.println("Create engine " + key);
    }else {
        System.out.println("Using engine " + key);
    }

    return _engines.get(key);
}

I want to be sure that each of the engines has its separate counter and file list. However after I ran the following code, it seems they share counter and list:

TestFileEngine.getInstance("http").generateFile();
TestFileEngine.getInstance("http").generateFile();
TestFileEngine.getInstance("http").generateFile();
TestFileEngine.getInstance("ftp").generateFile();
TestFileEngine.getInstance("ftp").generateFile();

System.out.println(TestFileEngine.getInstance("http").findFileByName("4.txt").getFileName());
System.out.println(TestFileEngine.getInstance("ftp").findFileByName("2.txt"));

Console:

Create engine http
Generated file 1.txt
Using engine http
Generated file 2.txt
Using engine http
Generated file 3.txt
Create engine ftp
Generated file 4.txt
Using engine ftp
Generated file 5.txt
Using engine http
4.txt
Using engine ftp
null

What should I do to the counter and generatedFileList fields so that each TestFileEngine created from the Multiton is completely separated?


Solution

  • Actually you declare the 3 fields of the class with the static modifier :

    private static int counter = 0;
    private static List<GeneratedFile> generatedFileList;
    private static final Map<String, TestFileEngine> _engines = new HashMap<String, TestFileEngine>();
    

    static fields are shared among all instances of the current class.
    You want that for engines field.
    But you don't want that for counter and generatedFileList fields that have to be attached to a specific instance of TestFileEngine. So make them instance fields instead of static.

    As a side note, int fields are by default valued to 0 and you should avoid _ to prefix your variables that doesn't make part of the naming conventions.
    So you could write :

    private int counter;
    private List<GeneratedFile> generatedFileList;
    private static final Map<String, TestFileEngine> engines = new HashMap<String, TestFileEngine>();