Лучший способ создать новый экземпляр на основе предварительных условий - PullRequest
4 голосов
/ 30 марта 2009

Привет, мой вопрос такой. Предположим, у вас есть интерфейс, который определяет, как будет реализован конвертер. Этот интерфейс может иметь метод loadDocument () и другой convert (). Теперь предположим, что мы хотим иметь несколько конвертеров, ConvertHTML2DOC, ConvertXML2HTML e.t.c, у вас есть идея.

Предположим, вы получаете два формата, которые вам нужно конвертировать, в 2 строки (не имеет значения, КАК вы их получили). Теперь вам нужно создать экземпляр вашего конвертера и конвертировать документы.

Мой вопрос: что лучше: на самом деле создать сложный оператор if или загрузить классы посредством отражения с помощью класса Class? чтобы продемонстрировать, я написал небольшой пример того, что я имею в виду. В моем примере у меня есть 2 класса P1 и P2, которые совместно используют интерфейс. Я также создаю 50000 из них, чтобы показать разницу в скорости. Как оказалось, нормальный вызов немного быстрее, но я думаю, что в сложном примере, таком как тот, который я упоминал в начале, преимущества создания классов с помощью метода Class более удобны и более удобны в обслуживании. Что ты думаешь?

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";
}
  }

Ответы [ 3 ]

6 голосов
/ 30 марта 2009

Вы обязательно должны следовать советам Хавьера - реестр фабрик - верный способ решить эту проблему. Я реализовывал это много раз в прошлом, например, для преобразования формата или какого-либо другого расширяемого фабричного решения на основе предикатов (например, для автоматической генерации графического интерфейса на основе информации об отражении).

Я бы, однако, предложил одно дополнение к дизайну - введение модели общей предметной области (CDM), которая предназначена для всех переводчиков. Скажем, у вас есть форматы A, B и C и вам нужно поддерживать преобразование между ними - вы получите все перестановки:

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

По мере роста числа форматов вы получаете взрыв преобразований! Лучшая идея - разделить каждое преобразование на две части - назовем его импортером и экспортером . Импортер преобразует формат в модель общего домена (CDM), а экспорт преобразует из CDM в некоторый формат.

В качестве примера мы разбиваем преобразование из А в В в следующее:

  • A -> CDM (это импорт)
  • CDM -> B (это экспорт)

Теперь, когда вы хотите добавить новый формат, вам нужно только написать импортер и экспортер, но вы получите перевод в / из всех других форматов! Разговор о расширяемом! Он также учитывает форматы, для которых вы можете читать, но не писать, и наоборот.

Таким образом, решение было бы иметь реестр заводов-импортеров и реестр заводов-экспортеров.

3 голосов
/ 30 марта 2009

обязательно используйте фабричный метод; но вместо "большого if" или искажения имен используйте метод "регистрации классов". для этого класс фабрики поддерживает таблицу, в которой каждый конкретный класс регистрирует свое условие (в вашем случае это имена источника и цели) и конструктор (это словарь 'condition-> constructor'). сама фабрика просто создает ключ словаря и выбирает нужный конструктор.

Одно большое преимущество заключается в том, что каждый конкретный класс инкапсулирует условие, которое он решает, и вы можете добавлять больше, не изменяя фабричный метод.

0 голосов
/ 30 марта 2009

Попробуйте что-то вроде этого, соберите безопасность времени, с одним бельем 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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...