Нужна помощь, чтобы эти классы использовали Шаблон посетителя и дженерики - PullRequest
1 голос
/ 28 мая 2010

Мне нужна помощь в разработке и реализации шаблона посетителя. Мы используем тонны instanceof, и это боль. Я уверен, что это можно изменить, но я не уверен, как это сделать.

В основном у нас есть интерфейс ProcessData

public interface ProcessData {
  public setDelegate(Object delegate);
  public Object getDelegate();
  //I am sure these delegate methods can use generics somehow
}

Теперь у нас есть класс ProcessDataGeneric, который реализует ProcessData

public class ProcessDataGeneric implements ProcessData {
  private Object delegate;

  public ProcessDataGeneric(Object delegate) {
    this.delegate = delegate;
  }
}

Теперь новый интерфейс, который получает ProcessData

interface ProcessDataWrapper {
  public ProcessData unwrap();
}

Теперь общий абстрактный класс, который реализует обертку, чтобы можно было получить ProcessData

@XmlSeeAlso( { ProcessDataMotorferdsel.class,ProcessDataTilskudd.class })
public abstract class ProcessDataCommon implements ProcessDataWrapper {
  protected ProcessData unwrapped;

  public ProcessData unwrap() {
    return unwrapped;
  }
}

Теперь реализация

public class ProcessDataMotorferdsel extends ProcessDataCommon {

  public ProcessDataMotorferdsel() {
    unwrapped = new ProcessDataGeneric(this);
  }
}
* * Аналогично тысяча двадцать-один * * 1 022
public class ProcessDataTilskudd extends ProcessDataCommon {

  public ProcessDataTilskudd() {
    unwrapped = new ProcessDataGeneric(this);
  }
}

Теперь, когда я использую эти классы, мне всегда нужно делать instanceof

ProcessDataCommon pdc = null;
if(processData.getDelegate() instanceof ProcessDataMotorferdsel) {
   pdc = (ProcessDataMotorferdsel) processData.getDelegate();
} else if(processData.getDelegate() instanceof ProcessDataTilskudd) {
   pdc = (ProcessDataTilskudd) processData.getDelegate();
}

Я знаю, что есть лучший способ сделать это, но я понятия не имею, как я могу использовать Дженерики и Шаблон Посетителя. Любая помощь приветствуется.

UPDATE

Я хочу добавить, что эти классы являются просто фрагментами гораздо большей реализации. ProcessData и ProcessDataGeneric - это то, что находится за пределами делегатов (ProcessDataMotorferdsel и т. Д.). Делегаты все расширяются ProcessDataCommon.

Я могу согласиться с тем, что рефакторинг, вероятно, лучше всего выполнять, но это рабочий код, которому 2 года, и он требует больших затрат на рефакторинг (время, тестирование и т. Д.). Тем не менее, я готов сделать это.

ОБНОВЛЕНИЕ № 2

Я попытался запустить общий процесс, но получаю ошибку компиляции. Вот как это выглядит сейчас.

public interface ProcessData<T extends ProcessDataCommon> {
  T getDelegate();
  setDelegate(T delegate);
}

public class ProcessDataGeneric<T extends ProcessDataCommon> implements ProcessData<T> {
  private T delegate;
  //Getter & setter
  public ProcessDataGeneric(T delegate) {
    this.delegate = delegate;
  }
}

public class ProcessDataMotorferdsel extends ProcessDataCommon {
  public ProcessDataMotorferdsel() {
    unwrapped = new ProcessDataGeneric<ProcessDataMotorferdsel>(this);
  }
}

Я получаю сообщение об ошибке компиляции: unwrapped = new ProcessDataGeneric<ProcessDataMotorferdsel>(this); Saying

[javac] ProcessDataMotorferdsel.java:52: incompatible types [javac] found : ProcessDataGeneric<ProcessDataMotorferdsel> [javac] required: ProcessData<ProcessDataCommon> [javac]

Я не могу сделать заголовки или хвосты этого сообщения об ошибке. Класс ProcessDataMotorferdsel расширяет ProcessDataCommon, поэтому IMO должен работать.

Ответы [ 4 ]

1 голос
/ 28 мая 2010

Этот ответ, вероятно, слишком упрощен, но я думаю, что почти весь код в вашем вопросе будет исключен идеальным решением. Проблема людей, пытающихся ответить на этот вопрос, состоит в том, что реальная проблема, которую пытается решить код, не ясна из того, что осталось.

Обычный подход к рефакторингу instanceof заключается в использовании подклассов в сочетании со стилем интерфейса «не спрашивать». Нет необходимости запрашивать у ProcessDataGeneric его делегата, если вы можете указать ProcessDataGeneric выполнить всю задачу за вас, например:

public interface ProcessData {
    public <T> T process(Data data);
}

public class ProcessDataGeneric implements ProcessData {
    private ProcessData delegate;

    public ProcessDataGeneric(ProcessData delegate) {
        this.delegate = delegate;
    }

    public <T> T process(Data data) {
        return delegate.process(data);
}

Я даже не уверен, что вам действительно нужен ProcessDataGeneric, поскольку все, что он делает, это содержит настоящий подкласс ProcessData:

public class ProcessDataMotorferdsel implements ProcessData {

    // Process the data the Motorferdsel way.
    public <T> T process(Data data) { ... }
}

public class ProcessDataTilskudd implements ProcessData {

    // Process the data the Tilskudd way.
    public <T> T process(Data data) { ... }
}

... и тогда вы можете использовать подклассы следующим образом:

ProcessData processor = new ProcessDataMotorferdsel();
Result      result    = processor.process(data);

... не беспокоясь о делегатах и ​​их типе.

Часто лучше использовать фабричный класс вместо конструктора для получения экземпляра подкласса, особенно если необходимо вычислить правильный подкласс.

0 голосов
/ 31 мая 2010

Я заставил это работать.

Посмотрите на ОБНОВЛЕНИЕ # 2 и включая это изменение:

public abstract class ProcessDataCommon<T extends ProcessDataCommon<?>> implements ProcessDataWrapper {
}

Сделано все компилировать.

0 голосов
/ 28 мая 2010

Вам не нужно кастовать, если ваш объект уже расширяет желаемый целевой класс. Я имею в виду, вы можете сделать это:

public interface ProcessData {
    public void setDelegate(ProcessDataCommon delegate);
    public ProcessDataCommon getDelegate();
}

и это:

public class ProcessDataGeneric implements ProcessData {
    private ProcessDataCommon delegate;
    public ProcessDataGeneric(ProcessDataCommon delegate) {
        this.delegate = delegate;
    }
    @Override
    public ProcessDataCommon getDelegate() {
        return delegate;
    }
    @Override
    public void setDelegate(ProcessDataCommon delegate) {
        this.delegate = delegate;
    }
}

И ваш экземпляр гребня упрощается до:

ProcessDataCommon pdc = processData.getDelegate();
0 голосов
/ 28 мая 2010

Может быть, я тоже что-то упускаю, но

ProcessDataCommon pdc = null;
if(processData.getDelegate() instanceof ProcessDataCommon) {
   pdc = (ProcessDataCommon) processData.getDelegate();
}

должно быть эквивалентно ..? Как вы упомянули, делегат всегда имеет тип ProcessDataCommon.

Если бы ProcessData#getDelegate() вернул бы ProcessDataCommon, вы могли бы также удалить оставшуюся проверку instanceof.

...