I've been trying to create a factory (in Java) that prohibits subclassing of the factory product and prevents the concrete factories from being used by anyone except the abstract factory. What do you think of this...
class Client
{
public static void main(String[] args)
{
Animal animal = Animal.create(Lion.class);
animal.doGrowl();
animal = Animal.create(Cow.class);
animal.doGrowl();
}
}
final class Animal
{
private Growl growl = null;
private Animal()
{
}
public void doGrowl()
{
growl.exec();
}
public abstract class Growl
{
public abstract void exec();
public final void initialize()
{
if (growl != null)
{
throw new IllegalStateException("Already initialized.");
}
growl = this;
}
}
public abstract static class Factory
{
public Factory(FactoryInitializer initializer)
{
initializer.validate();
}
public abstract void attachGrowl(Animal a);
}
public static final class FactoryInitializer
{
private boolean isValid = true;
private FactoryInitializer()
{
}
public void validate()
{
if (isValid)
{
isValid = false;
}
else
{
throw new IllegalStateException("FactoryInitializer is not valid.");
}
}
}
public static Animal create(Class<? extends Factory> factoryClass)
{
try
{
Constructor constructor = factoryClass.getConstructor(new Class[]{FactoryInitializer.class});
Factory factory = (Factory) constructor .newInstance(new Object[]{new FactoryInitializer()});
Animal animal = new Animal();
factory.attachGrowl(animal);
if (animal.growl == null)
{
throw new IllegalStateException("Animal factory did not initialize Growl.");
}
return animal;
}
catch (Exception e)
{
return null;
}
}
}
class Lion extends Animal.Factory
{
public Lion(Animal.FactoryInitializer initializer)
{
super(initializer);
}
public void attachGrowl(Animal a)
{
a.new Growl()
{
public void exec()
{
System.out.println("rarrrrrr");
}
}.initialize();
}
}
class Cow extends Animal.Factory
{
public Cow(Animal.FactoryInitializer initializer)
{
super(initializer);
}
public void attachGrowl(Animal a)
{
a.new Growl()
{
public void exec()
{
System.out.println("mmooooo");
}
}.initialize();
}
}