Search code examples
javaspringfactory-pattern

Spring DI, factory services


This is more of a design question than a code implementation but I am hoping someone can help out.

The issue I am facing is I will need to inject a modelSerivce depending on two inputs. I am pretty sure based on some documentation and SO questions I want to use a factorybean for this.

My question goes more into the construction of these classes that will be created with the factorybean. How can I re-use a singleton bean with the factory instead of creating a new class every time the factory is called?

Here is what the code looks like:

Thing Interface:

public interface Thing {
    void Run();
}

ThingA Implementation:

public class ThingA implements Thing{
    public void Run() {
        System.out.println("In ThingA");
    }
}

ThingB Implementation:

public class ThingB implements Thing{
    public void Run() {
        System.out.println("In ThingB");
    }
}

ThingFactory Implementation:

public class ThingFactory {
    public Thing GetThing(String stateCode, Date date){
        Thing result;
        if(stateCode == "MA") {
            result = new ThingA();
        }
        else {
            result = new ThingB();
        }
        return result;
    }
}

What i really want the factory to do is pull a known implementation instead of creating an implementation each time the factory is called. I also would like to not tie my factory to the Spring framework by doing something like this:

ApplicationContext.getBean()

Solution

  • Just keep the beans in spring context as singletons as you normally would. Inject the context into your factory and pull beans from it based on your business rules.

    public class ThingFactory {
    
        @Autowired 
        private ApplicationContext ctx;
    
        public Thing GetThing(String stateCode, Date date){
            Thing result;
            if(stateCode == "MA") {
                result = ctx.getBean("someBean")
            }
            else {
                result = ctx.getBean("someOtherBean")
            }
            return result;
        }
    }
    

    You can be even more clever and use a scheme to name the beans on the context:

    @Service("Thing_MA")
    public class ThingA implements Thing{
    .
    .
    .
    }
    

    This buys you nice declarative look up rules for your factory:

    public class ThingFactory {
        public Thing GetThing(String stateCode, Date date){
            return (Thing) ctx.getBean("Thing_" + stateCode);
    
       }
    }