1

I've read this topic with the instruction about how to use factory pattern
factory pattern dynamic approach

I have this in my factory

public class FilterFactory {
    static Map<String, Class> creators;

    static {
        creators = new HashMap<>();
    }
    /*...*/
}

And this is one of the classes, which I want to put in the factory

public class ExtentionFilterSerializer implements FilterSerializer {
    static {
            FilterFactory.creators.put(".", ExtentionFilterSerializer.class);
    }
    /*...*/
}

When I try to use factory in the program, I see that Map is empty. What did I do wrong?

Community
  • 1
  • 1
pFlam
  • 13
  • 3
  • How do you use the factory? – f1sh Apr 06 '17 at 08:34
  • 1
    I guess you may want to read "Service Provider" in Java instead of reinventing the wheel – Adrian Shum Apr 06 '17 at 08:39
  • @AdrianShum I think a link would be useful here. – Axel Apr 06 '17 at 09:15
  • @Axel I have already given the keyword, it shouldn't be anything difficult to Google it and pick up whatever suitable to oneself – Adrian Shum Apr 06 '17 at 09:46
  • @AdrianShum I had to look myself, and the right link is somewhat down the list. I think it should be [this](https://docs.oracle.com/javase/tutorial/ext/basics/spi.html) one, right? There came lots of links about outdated docs first. – Axel Apr 06 '17 at 11:53
  • Yup. This is the first result when I googled "Java service provider" – Adrian Shum Apr 06 '17 at 12:01

2 Answers2

0

For code related:

The static block to register your class ExtentionFilterSerializer will execute if only if this unit ExtentionFilterSerializer is realized on classpath. If you haven't used this class ExtentionFilterSerializer anywhere in your program, this class wont be loaded, hence the static registration.

Adding a Class.forname("ExtentionFilterSerializer") to your client application will fix the load issue.

Regarding approach used:

The objective of using a Factory pattern is to determine and create an instance of concrete object type especially when the application cannot determine that at compile time. By adding a static initializer to dynamically register your concrete class, your Factory know of its existence, but still cannot determine which concrete class to use.

Besides, the registration part FilterFactory.creators.put(".", ExtentionFilterSerializer.class); should never be in ExtentionFilterSerializer, but in a client program.

Another variant of a Factory design pattern makes the creation method abstract (not same as AbstractFactory pattern). A concrete object can be created using those concrete Factory classes, which looks close to your case. Read this.

Rajeev Sreedharan
  • 1,753
  • 4
  • 20
  • 30
  • This works but it's very bad form to encourage use of a static initializer block in the `ExtentionFilterSerializer` class. Can you suggest another solution, without that static block? – vikingsteve Apr 06 '17 at 08:49
0

static blocks are (at least in my experience) not very reliable in terms of execution. You have no direct control over when the static block of ExtentionFilterSerializer is executed.

I'd rather implement an init() method, so you can explicitly call ExtentionFilterSerializer.init() (maybe even add an argument telling the method to which factory this one should be added).

I just worked with a similar construction and static blocks were very unreliable resulting in me placing them in a controlled init() function, which is called early. From this point in time, you can be sure that your factory and your ExtentionFilterSerializer are properly initialized.

Edit: Post below me explains in detail what happens. Either implement a init() or make sure your class is actually somewhere.

GxTruth
  • 509
  • 4
  • 9