I have a simple Factory (GenericFudge) that creates objects of different types depending on external circumstances. Currently, my code looks something like this:
abstract class Fudge {
Fudge() {
}
void make() {
System.out.println("made.");
}
}
class VanillaFudge extends Fudge {
@Override
void make() {
System.out.print("Vanilla ");
super.make();
}
}
class ChocolateFudge extends Fudge {
@Override
void make() {
System.out.print("Chocolate ");
super.make();
}
}
class InvalidFlavorException extends Exception {};
// factory / proxy
public class GenericFudge {
Fudge mFudge = null;
GenericFudge(String flavor) throws InvalidFlavorException {
if (flavor.equals("Chocolate")) {
mFudge = new ChocolateFudge();
} else if (flavor.equals("Vanilla")) {
mFudge = new VanillaFudge();
}
}
void make() {
mFudge.make();
}
public static void main(String args[]) {
for (String flavor : new String[] {"Chocolate", "Vanilla"}) {
GenericFudge fudge;
try {
fudge = new GenericFudge(flavor);
fudge.make();
} catch (InvalidFlavorException e) {
System.out.println("Sorry, we don't make that flavor");
}
}
}
}
My goal is to get the details of chocolate and vanilla out of GenericFudge, so that when CaramelFudge is implemented, no changes to GenericFudge are required. For example, GenericFudge would iteratively call a "createIfItsMyFlavor()" method for every xxxFudge class. (In my actual application, I have to try them iteratively, but I'd be interested in other possibilities.)
My instinct was to use a static initializer per subclass (per xxxFudge) that adds "itself" to a list by calling a registerFudge method of GenericFudge, but this hits the chicken-and-egg problem (the class is never used, so its static initializer never gets invoked).
No doubt there's a better way I haven't envisioned. Thanks!
If you are using any kind of dependency injection system like Spring, this is easy to implement using @PostConstruct. If this works, then you can call a register method in GenericFudge from the method you annotate with PostConstruct. In GenericFudge, you have a map, and whenever addType is called you add it to the map. That way your GenericFudge remains unchanged, and new callers will register using PostConstruct. To simplify things further, you can define this in your base class Fudge, and pass the fudge name using the constructor, that way you don't have to declare the register method in each sub-class.
private String fudge;
public Fudge(final String fudge) {
this.fudge = fudge;
}
@Autowired
private GenericFudge fudge;
@PostConstruct
private void register() {
fudge.addType(fudge, this);
}
In GenericFudge
private Map<String, Fudge> fudgeTypes = Maps.newHashMap();
public void register(final String fudgeType, final Fudge fudgeInstance) {
fudgeTypes.put(fudgeType, fudgeInstance);
}
If you do not use any dependency injection system: Another approach could be to have a static method in the base class Fudge, where you declare all the types of fudge and then return an instance based on the request. That way you don't modify the GenericFudge class, but only the base class of Fudge. This is not ideal, but it gets you away from having to modify the GenericFudge class, and instead of "registering" with something like PostConstruct, you put an entry into the Map.
Example (ImmutableMap from Guava, you can declare the map however you like , this is only for the example):
public abstract class Fudge {
private static final Map<String, Fudge> FUDGE_TYPES = ImmutableMap.of(
"Type1", new Type1Fudge(),
"Type2", new Type2Fudge()
// Add new entry when implemented
);
public static Fudge getFudge(final String fudge) {
if (FUDGE_TYPES.containsKey(fudge)) {
return FUDGE_TYPES.get(fudge);
} else {
// handle missing fudge depending on your preference
}
}
}