Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best method to create a new instance based on precondition

Tags:

java

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";
}
  }
like image 823
Savvas Dalkitsis Avatar asked Mar 01 '23 21:03

Savvas Dalkitsis


1 Answers

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.

like image 145
Daniel Paull Avatar answered Mar 30 '23 00:03

Daniel Paull