Jarek
Jarek

Reputation: 7729

Restricting instantiation of a class to a single Factory

I have a class and I would like to restrict its instantiation to only one Factory class, how can I prevent this object from instantiation by the new operator?

Upvotes: 1

Views: 5650

Answers (6)

Andreas Dolk
Andreas Dolk

Reputation: 114817

Another pattern that starts replacing the Singleton pattern is using enums:

public enum Factory { INSTANCE;

  public SomeObject create(Params someParams) {
    if (isTypeA(someParams))
      return new TypeASubclass();
    else if (isTypeB(someParams))
      return new TypeBSubclass();
    else
      return new SomeDefaultObject();
  }
}

You'll use it in the standard way:

SomeObject someObject = Factory.INSTANCE.create(myParams);

and the enum gurantees that there is only one factory on the system *)


*) with usual one-per-jvm / one-per-classloader limitations

Upvotes: 0

Peter Lawrey
Peter Lawrey

Reputation: 533870

A simpler approach is to trust other class in the same package.

This way the constructor has default scope and the Factory has to be in the same package.


However to answer your question, you can make the Factory an inner class it can call the private constructor of an outer class. e.g.

public class MyObject {
    private MyObject() { }

    public static class Factory {
         public MyObject create() {
             // can call private members of the outer class.
             return new MyObject(); 
         }
    }
}

MyObject.Factory mof = new MyObject.Factory();
MyObject mo = mof.create();

Using this approach restricts access to inner and nested classes.

Upvotes: 1

Nathan Ryan
Nathan Ryan

Reputation: 13051

You can make the class an inner (member) class of the factory class. The singleton factory instance should be accessible only within the factory class. The factory method should return a public interface implemented by the inner class.

public interface FooBehavior { ... }
public class FooFactory {
    private static FooFactory factory = new FooFactory();
    private FooFactory() { ... }
    class Foo implements FooBehavior { ... }
    public static FooBehavior createFoo() {
        return factory.new Foo();
    }
}

The instances of Foo are tied to the lifecycle of the factory. Because an instance of the factory is required to create an instance of Foo, and because the only instance of the factory is contained within the factory, only the factory can create instances of Foo.

As an aside, be careful when using the singleton pattern.

Upvotes: 1

Alan Escreet
Alan Escreet

Reputation: 3559

Jigar is correct given your question, however, this is an anti-pattern with serious drawbacks:

http://en.wikipedia.org/wiki/Singleton_pattern#Drawbacks

http://blogs.msdn.com/b/scottdensmore/archive/2004/05/25/140827.aspx

I would suggest that you have a look at the design of the system before you implement such a solution. It should never be necessary to lock instantiation of a class away in the way you descibed.

Upvotes: 1

spot35
spot35

Reputation: 888

It's not going to be easy. You could make the constructor package local and then situate the factory in the same package. This would still mean that classes defined in the same package could still instantiate that class.

You could interrogate the stack from the current thread by doing something like -

StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
if ("your.package.Factory".equals(stackTrace[1].getClassName()) {
    instantiateClass();
} else {
    throw new InstantiationException("Can only instantiate from factory");
}

You'll have to check which StackTraceElement it is, but this should work.

Upvotes: 0

duffymo
duffymo

Reputation: 309008

Make all the constructors package private; have a single Factory object in that same package that owns the public creation method for clients outside the package.

It's easy to do with a single class, but I'm imagining that you'd like that Factory to be virtual: able to create several implementations of a single interface or abstract class.

Upvotes: 5

Related Questions