views:

79

answers:

3

I have a factory class that serves out a bunch of properties.

Now, the properties might come either from a database or from a properties file.

This is what I've come up with.

public class Factory {

    private static final INSTANCE = new Factory(source);

    private Factory(DbSource source) {
        // read from db, save properties
    }

    private Factory(FileSource source) {
        // read from file, save properties
    }

    // getInstance() and getProperties() here
}

What's a clean way of switching between these behaviors based on the environment. I want to avoid having to recompile the class each time.

A: 

You could define something like:

public abstract Factory  {
    // define all factory methods here
    public static Factory getFactory(FactoryType type, Object source) {
        if (type == FactoryType.DB) {
             return new DbFactory(source);
        }
        if (type == FactoryType.PROPERTIES) {
             return new PropertiesFactory(source);
        }
    }
}

public DbFactory implements AbstractFactory { .. }
public PropertiesFactory implements AbstractFactory { .. }
  • You can use instanceof instead of the enum
  • if you want your factories to be singleton, replace instantiation (new) with a static method
Bozho
+6  A: 

Dependency Injection is the way to do it.

Generally, using dependency injection in your situation would look like this (example is for Spring DI, would look little different for Guice but the idea is the same):

public interface Factory {
    Properties getProperties();
}

public class DBFactory implements Factory {
    Properties getProperties() {
        //DB implementation
    }
}

public class FileFactory implements Factory {
    Properties getProperties() {
        //File implementation
    }
}

public SomeClassUsingFactory {
    private Factory propertyFactory;

    public void setPropertyFactory(Factory propertyFactory) {
        this.propertyFactory = propertyFactory;
    }

    public void someMainMethod() {
        propertyFactory.getProperties();
    }
}

//Spring context config
<!-- create a bean of DBFactory (in spring 'memory') -->
  <bean id="dbPropertyFactory"
    class="my.package.DBFactory">
    <constructor-arg>
      <list>
        <value>Some constructor argument if needed</value>
      </list>
    </constructor-arg>
  </bean>
 <!-- create a bean of FileFactory (in spring 'memory') -->
  <bean id="filePropertyFactory"
    class="my.package.FileFactory">
    <constructor-arg>
      <list>
        <value>Some constructor argument if needed</value>
      </list>
    </constructor-arg>
  </bean>
<!-- create a bean of SomeClassUsingFactory -->
  <bean id="MainClass"
    class="my.package.SomeClassUsingFactory">
    <!-- specify which bean to give to this class -->
    <property name="propertyFactory" ref="dbPropertyFactory" />
  </bean>

Then, in different environment you just swap your xml config file with some other file that sets the property to filePropertyFactory and you get it passed into the SomeClassUsingFactory.

Max
Or, you introduce a `Factory()` constructor which inspects the environment properties and calls the file- or database constructor respectively.
rsp
Would work if you will have only 2 ways to get properties for whole lifetime of the project. But if you need to add some TCPPropertyFactory, or XmlPropertyFactory? You would have to change that Factory() constructor each time a new PropertyFactory appears. And with proper IoC mechanism - you would only need to edit a configuration file for that.
Max
+1  A: 

Simply, don't use singletons.

"Parameterise from Above." Construct the required implementation at a place in the code where it makes sense. Pass the instance down to those objects that need it as they are constructed.

Tom Hawtin - tackline
I want to add that this is called Inversion of Control, Dependency Injection being a subtype of IoC. You can read more about it here: http://en.wikipedia.org/wiki/Inversion_of_control
Max
@Max "IoC" is typically used in the context of [true] frameworks (where the library is the application, upcalling into the "business" code). I mean a simple, globalless style of code with a sort of strict hierarchical structure of implementation(ish) dependency. IYSWIM. I certainly would advise against ploughing in with Dependency Injection frameworks (until they make sense).
Tom Hawtin - tackline
Yes, it is 'typically' used in that context. But doing it all manually does not make it *not* IoC.
Max
@Max I think IoC implies callbacks, which is not what is happening here.
Tom Hawtin - tackline
"Paraterise ..." -- should that be paraMEterise?
mlvljr