tags:

views:

131

answers:

3

Hi my question is this. Suppose you have an interface that defines how a converter would be implemented. This interface could have a method loadDocument() and another convert(). Now suppose we want to have multiple converters, ConvertHTML2DOC, ConvertXML2HTML e.t.c you get the idea.

Suppose you get the two formats you need to convert in 2 strings (doesn't matter HOW you get them). Now you need to create an instance of your converter and convert the documents.

My question is which is better: to actually create an elaborate if statement or to load the classes through reflection with the Class class? to demonstrate I wrote a little example of what I mean. In my example I have 2 classes P1 and P2 that share an interface. I also create 50000 of them to show of the speed differences. As it turns out the normal invocation is slightly faster, but I think that in a complex example such as the one i mentioned in the beginning, the benefits of creating the classes through the Class method is more convenient and more maintainable. What are your thoughts?

import java.util.*;

public class Test {
  public static void main(String[] args) {
    try {
      Random r = new Random();
      Date test1start = new Date();
      for (int i = 0; i<= 50000; i++){
        Printable p = (Printable)Class.forName("P"+(r.nextInt(2)+1)).newInstance();
        System.out.println(p.getString());
      }
      Date test1stop = new Date();
      Date test2start = new Date();
      for (int i = 0; i<= 50000; i++){
        Printable p;
        if (r.nextInt(2) == 0 ) {
           p = new P1();
        } else {
          p = new P2();
        }
        System.out.println(p.getString());
      }
      Date test2stop = new Date();
      System.out.println("Test with 'Class' invocation took "+(test1stop.getTime()-test1start.getTime())+" milliseconds.");
      System.out.println("Test with 'normal' invocation took "+(test2stop.getTime()-test2start.getTime())+" milliseconds.");
    } catch (Exception e) {
    }
  }
}

interface Printable {
  public String getString();
}

class P1 implements Printable {
  public String getString(){
    return "1";
  }
}

class P2 implements Printable {
  public String getString(){
    return "2";
}
  }
A: 

Try something like this, compile time safety, with the one-lineness of the Class.forName.

public class PrintableFactory
{
    public enum Type
    {
        HTML,
        DOC,
    }

    public static Printable getPrintable(final Type from, final Type to)
    {
        final Printable printable;

        if(from == HTML && to == DOC)
        {
            printable = new HtmlToDoc();
        }
        else if(from == DOC && to == HTML)
        {
            printable = new DocToHTML();
        }
        else
        {   
            // you decide if you want runtime or compile time exception handling
            // could also return numm but I don't like that myself.
            throw new ImpossibleConversionException(from, to);
        }

        return (printable);
    }
}
TofuBeer
ok... and you don't like this because? How do you expect people to learn something from the down voting if you don't say why you are down voting it?
TofuBeer
Possibly because it lacks the extensibility that the OP is clearly trying to achieve?
Daniel Paull
+3  A: 

definitely use a factory method; but instead of a "big if", or name-mangling, use a "class registration" method. for this, the factory class maintains a table where each concrete class registers it's condition (in your case it's the source and target names) and the constructor (it's a 'condition->constructor' dictionary). the factory itself simply constructs the dictionary key and fetches the constructor it needs.

one big advantage is that each concrete class encapsulates the condition it solves, and you can add more without changing the factory method.

Javier
+5  A: 

You should definitely follow the advice from Javier - a registry of factories is the right way to go for this problem. I've implemented it that way many times in the past, be for format translation or some other extensible "predicate" based factory solution (eg, automatic GUI generation based on reflection information).

I would however suggest one addition to the design - the introduction of a common domain model (CDM) that is targeted by all translators. Say that you have formats A, B and C an that you need to support transformation between each - you get all the permutations:

  • A -> B
  • A -> C
  • B -> A
  • B -> C
  • C -> A
  • C -> B

As the number of format grows, you get an explosion of transformations! A better idea is to separate each transformation into two parts - lets call it an importer and an exporter. The importer converts a format to the common domain model (CDM) while an export converts the from the CDM to some format.

As an example, we decompose the conversion from A to B into the following:

  • A --> CDM (this is an import)
  • CDM --> B (this is an export)

Now when you want to add a new format you need only write an importer and an exporter but you get translation to/from all other formats! Talk about extensible! It also allows for formats for which yo can read but not write and vice versa.

So, the solution would be to have a registry of importer factories and a registry of exporter factories.

Daniel Paull