Search code examples
javaxmlxsdabstract-factory

Dynamically select which objects to create with an abstract factory pattern from xml


I hope this isn't too specific.

I have created an XML Schema which i compile with XJC to get the classes. The XML basically represents some elements of a form (like textfield, labels etc). Here's an excerpt:

<gruppoOggetti id="string" nome="string">
        <oggetto xsi:type="labelType" etichetta="string" id="string" obbligatorio="false" />
        <oggetto xsi:type="listaOpzioni" id="string" obbligatorio="1">
            <opzione id="string">string</opzione>
        </oggetto>
        <oggetto xsi:type="imageType" etichetta="string" id="string" obbligatorio="0" />
    </gruppoOggetti>

As you can see, the type of the object is defined from the attribute xsi:type so when i unmashall the xml with jaxb it automatically instatiate the right classes.

Now the question: I'm implementing an abstract factory to create the objects so basically i have to call a different method of my factory depending on which object the xml says i have to create. The not-quite-satisfying method i'm using right now is this:

public OggettoBase creaOggetto(Factory f, OggettoType oggetto)
{
    String tipo = oggetto.getClass().getSimpleName().toString();
    OggettoBase ret = null;
    switch(tipo)
    {
        case "CheckBoxType": ret = f.createCheckbox(); break;
        case "ImageType":ret = f.createImage(); break;
        case "LabelType":ret = f.createLabel(); break;
        case "LinkType": ret = f.createLink(); break;
        case "ListaOpzioni": ret = f.createLista(); break;
        case "PasswordType": ret = f.createPassword(); break;
        case "RadiobuttonType": ret = f.createRadiobutton(); break;
        case "TextareaType": ret = f.createTextarea(); break;
        case "TextfieldType": ret = f.createTextfield(); break;
        default : System.out.println("Il tipo: "+tipo+" non esiste");
    }
    return ret;
}

I want to use an other method (edit: instead of the switch/case), and i was thinking about enums but i'm not quite there. Also i don't want to use reflection.


Solution

  • Well, to me this is not really a factory pattern. A factory pattern usually is coupled with the Strategy Pattern. So my suggestion would be to implement your factory so that it just calls f.CreateObject which will return an object that can call a more generic Create(). The nice thing about this is that it allows your methods and classes to focus on their particular responsibility, and anytime you have to change functionality for one item, then you dont have to rebuild others (you can work off of a base class if you have common functionality that must change for all, too). Let me know if you need further details on below or the reasoning. You can probably even make the main method more of your factory and pretty the code even more.

    public OggettoBase creaOggetto(OggettoType oggetto)
    {
        String tipo = oggetto.getClass().getSimpleName().toString();
        IObjectCreator creator = Factory.GetObjectCreator(tipo);
        if(creator == null)
            return null;
        return creator.Create();
    }
    
    public interface IObjectCreator
    {
        OggettoBase Create(); 
    }
    
    public CheckboxCreator:IObjectCreator
    {
        public OggettoBase Create()
        {
            return new Checkbox();
        }
    }
    
    //Repeat for each type
    
    public static Factory
    {
        public IObjectCreator GetObjectCreator(string tipo)
        {
            IObjectCreator creator;
            switch(tipo)
            {
                case "CheckBoxType": creator = new CheckboxCreator(); break;
                ...
                default : System.out.println("Il tipo: "+tipo+" non esiste");
            }
            return creator;
    }
    

    PS. I come from a C# background, so if some of the syntax is off, please let me know. But, the basic principle still applies