views:

4232

answers:

7

I'm aiming to create a set of objects, each of which has a unique identifier. If an object already exists with that identifier, I want to use the existing object. Otherwise I want to create a new one. I'm trying not to use the word Singleton, because I know it's a dirty word here...

I can use a factory method:

    // A map of existing nodes, for getInstance.
private static Map<String, MyClass> directory = new HashMap<String, MyClass>();

public static MyClass getInstance(String name) {
    MyClass node = directory.get(name);
    if(node == null) {
       node == new MyClass(name);
    }
    return node;
}

Or equally, I could have a separate MyClassFactory method.

But I had intended to subclass MyClass:

public class MySubClass extends MyClass;

If I do no more, and invoke MySubClass.getInstance():

MyClass subclassObj = MySubClass.getInstance("new name");

... then subclassObj will be a plain MyClass, not a MySubClass.

Yet overriding getInstance() in every subclass seems hacky.

Is there a neat solution I'm missing?


That's the generalised version of the question. More specifics, since the answerers asked for them.

The program is for generating a directed graph of dependencies between nodes representing pieces of software. Subclasses include Java programs, Web Services, Stored SQL procedures, message-driven triggers, etc.

So each class "is-a" element in this network, and has methods to navigate and modify dependency relationships with other nodes. The difference between the subclasses will be the implementation of the populate() method used to set up the object from the appropriate source.

Let's say the node named 'login.java' learns that it has a dependency on 'checkpasswd.sqlpl':

this.addDependency( NodeFactory.getInstance("checkpasswd.sqlpl"));

The issue is that the checkpasswd.sqlpl object may or may not already exist at this time.

+2  A: 

The static method is defined on the parent class, and it's called statically as well. So, there's no way of knowing in the method that you've called it on the subclass. The java compiler probably even resolves the call statically to a call to the parent class.

So you will need to either reimplement the static method in your child classes as you propose, or make them not static so you can inheritance (on a hierarchy of factory objects, not classes), or pass a parameter to signify the type you want to create.

Check out the EnumSet.noneOf() method. It has a similar issue as you do, and it solves it by passing the java.lang.Class method. You could use newInstance on the class. But personally, I'd just use factory objects rather than classes with static methods.

Wouter Lievens
the 'probably' made me suspicous. Appears you're wrong. The compiler looks at the declared type of an instance to find the actual static method to call. This is called 'hiding' as opposed to 'overriding'. (-1)
xtofl
You're bullshitting here. First of all, there's no "instance", and second of all, he's specifically asking about not writing the static method in his subclasses. Next time you -1 me, read the context first.
Wouter Lievens
A: 

The pattern appears to be a sort of Flyweight (structurally, if not a perfect match for intent.)

The populate method, as described, could be mapped to the Template pattern, although it doesn't necessarily address the expressed concerns.

What I'd suggest is a generalization of the factory, with a create (instead of getInstance, which does imply a Singleton, to me anyway) method for the various types you expect.

public static MyClass createJavaNode(String name, <differentiator>);
public static MyClass createSqlPlNode (String name, <differentiator>);
.
.
.

The knowledge about how a name maps to a <differentiator> is really an implementation choice. Ideally, there'd be a polymorphic create and the differentiation would be by node type. The create method returns MyClass, but is really returning the subclasses. I'd strongly consider making MyClass either an interface or an abstract class, with an abstract populate method (there's the Template).

From the description, it really appears that it is the creation behavior that is different between the types, not the behavior of the sub-classes themselves, so the case for the refactoring of MyClass to an interface or abstract class gets stronger.

To paraphrase R. A. Heinlein, TANSTAAFL - There Ain't No Such Thing As A Free Lunch -- somewhere the knowledge of how to create the various types has to exist in the application. So your choice is to put that knowledge in the Factory class, as some of the other answers have expressed, or to separate the decision of what implementation is created from how it is created. It seems like there is a capability of scanning a file system and collecting the Nodes from it. That code will (or can) know the type of the Node (the answer to the what) that should be created.

Whether that gets implemented as a switch or in a more OO fashion (one of the times where table driven dispatch would be nice) is up to you. If this is something that might be useful, I can expand on that here.

BTW, if the core behavior of MyClass needs to be extended or modified for some subclasses, that is where a Decorator might be useful.


Original Answer:

You might consider the Decorator or Template Design Patterns as alternatives.

Ken Gentle
I'm struggling to see how either of these help. Can you expand on it?
slim
Fair enough - perhaps if you elaborate a bit on what you're trying to accomplish I can either give you a better answer or delete it. Like what does MySubClass add to MyClass?
Ken Gentle
OK thanks. I tried to generalise in order not to bog the question down in specifics. But I'll edit it to add those specifics now.
slim
+3  A: 

Have you looked into Guice? Not sure if it would solve your problem exactly, but it acts as a generic factory and dependency injection container, and eliminates non-type safe String keys.

bajafresh4life
Good suggestion. Most Dependency Injection/Inversion of Control containers do exactly what you are looking for.
erickson
+1  A: 

The Class class is parameterized with the type of instance it can create. (ex: Class<String> is a factory for String instances.)

I don't see any way to get around knowing the type of instance that should be created when you getOrCreate using the factory method here, so I would recommend passing it to the method and parameterizing on the type being generated:

private static Map<String, MyClass> directory = new HashMap<String, MyClass>();

public static <T extends MyClass> T getInstance(String name, Class<T> generatorClass)
{
  MyClass node = directory.get(name);    
  if(node == null) {
    node = generatorClass.getConstructor(String.class).newInstance(name);
    directory.put(name, node);
  }
  return node;
}

Also, I noticed you weren't actually putting the newly constructed nodes in the directory - I'm assuming that's an oversight. You could also overload this method with another that didn't take a generator and hardcoded to the default type:

public static MyClass getInstance(String name) {
  return getInstance(name, MyClass.class);
}
Alex Miller
+1  A: 

You seem to imply that somewhere you know what class it should be if it doesn't exist. If you implement that logic in your factory you should get the correct classes.

This way you should also have no need to know what class actually was returned from the factory.

I would also likely make 'MyClass' an interface if you're considering a Factory pattern.

pvgoddijn
A: 

You probably want dependency injection. It would generalize what you are trying to do somewhat.

Also, Inheritance is probably not exactly what you need either. Never use it unless you can pass the "is-a" test. If your inherited class "Has-a" unique string tag (essentially that's what your class seems to be) it doesn't mean it "is-a"...

You could have one hold the other though. There are probably quite a few solutions but A) you didn't post your entire list of requirements so we can but guess, and B) what you probably want is dependency injection. :)

Bill K
+2  A: 

after reading you explanation of the problem i think your making it very difficult for yourself by sub classing in your graph construction. i think the problem becomes much more simple if you separate the dependency graph from the "program information"

use an Interface such as:

Public Interface Node<T> {
  public Object<T>    getObject();
  public String       getKey();
  public List<String> getDependencies();
  public void         addDependence(String dep);
}

and then use a factory to instantiate you nodes

pvgoddijn
+1 because it matches the decision I came to after reading everyone else's good suggestions.
slim