views:

96

answers:

2

I have a requirement to create some objects that implement a given interface, where the type of concrete implementation being created is based on an Enum value.

I run into trouble when the different concrete implementations require different parameters at runtime.

This example (C#) is fine:

public enum ProductCategory
{
    Modem,
    Keyboard,
    Monitor
}

public class SerialNumberValidatorFactory()
{
    public ISerialNumberValidator CreateValidator(ProductCategory productCategory)
    {
        switch (productCategory)
        {
            case ProductCategory.Modem:
                return new ModemSerialNumberValidator();
            case ProductCategory.Keyboard:
                return new KeyboardSerialNumberValidator();
            case ProductCategory.Monitor:
                return new MonitorSerialNumberValidator();
            default:
                throw new ArgumentException("productType", string.Format("Product category not supported for serial number validation: {0}", productCategory))
        }
    }
}

However, what happens if the concrete implementations have different constructor arguments? I can't pass in all the values to the SerialNumberValidatorFactory.CreateValidator() method, so how do I proceed?

I've heard the Abstract Factory pattern is supposed to solve this, but I'm not sure how to implement it properly.

+1  A: 

You can always create a Settings container to pass to the CreateValidator method. Start with a base IValidatorSettings, then create IModemSerialNumberValidatorSettings etc, your CreateValidator could then take ProductType and IValidatorSettings arguments.

Your concrete classes for the validators would then take their IXXXValidatorSettings interface as the sole constructor argument.

Further to this you could then create an IValidatorSettings factory.

I think abstract factory is a factory that creates a factory to handle a given set of types - not sure if it would apply in your scenario.

Adam
+2  A: 

What you are using is a Factory Method pattern, what you should use is an Abstract Factory

In abstract factory, you provide a factory class for each concrete implementation:

So your code becomes: (forgive the code, but the rationale is same)

public class SerialNumberValidatorFactory
{
    public static SerialNumberValidatorFactory newInstance(
           ProductCategory productCategory)
    {
        switch (productCategory)
        {
            case ProductCategory.Modem:
                return new ModemValidatorFactory();
            ....
        }
    }

    public abstract ISerialNumberValidator createValidator();
}

public class ModemValidatorFactory extends SerialNumberValidatorFactory
{
   public ISerialNumberValidator createValidator() 
   {
      return new ModemSerialNumberValidator("model", "number");
   }
}

ISerialNumberValidator = SerialNumberValidatorFactory.newInstance(productCategory).createValidator()
naikus