2013-11-08 3 views
12

Я пытаюсь реализовать класс, который изолирует часть объекта и заменяет деталь чем-то другим. Часть объекта может не иметь того же типа, что и сам объект.Функция «состав» и тип безопасности в java

Простым примером может служить класс, который берет строку «--12--», изолирует номер ascii и заменяет ее следующим натуральным числом. Таким образом, вся последовательность будет "--12--" -> "12" -> 12 -> 13 -> "13" -> "--13--".

Имея это в виду, я осуществил следующее:

public abstract class Replacer<Outer, Inner> 
    { 
    protected abstract Inner decompose(Outer something); 
    protected abstract Outer compose(Inner something); 
    protected abstract Inner inner_replace(Inner something); 
    public Outer replace(Outer something) 
     { 
     Inner s = decompose(something); 
     s = inner_replace(s); 
     return compose(s); 
     } 
    } 

Теперь я хочу, чтобы иметь возможность составить серию заменителя - складывать их, так что каждый из них рассчитывает свою inner_replace с помощью «ниже "Заменитель:

public abstract class ComposableReplacer<Outer, Inner> extends Replacer<Outer, Inner> 
    { 
    protected Replacer<Inner, ?> child; 

    @Override 
    public Outer replace(Outer something) 
     { 
     Inner s = decompose(something); 
     s = inner_replace(s); 
     if (child!=null) 
      s= child.replace(s); 
     return compose(s); 
     } 

    } 

Так далеко, это работает правильно, но теперь я пытаюсь написать удобный метод, чтобы взять пар из ComposableReplacers и складываем их автоматически:

public static <I, O> ComposableReplacer<I, O> compose(ComposableReplacer<?, ?>... rs) 
    { 
    for (int i=0; i<rs.length-1; i++) 
     rs[i].child= rs[i+1]; 
    return rs[0]; 
    } 

Это не удается, так как внутренний тип каждого ComposableReplacer должен быть внешний вид своего ребенка и компилятор не может сделать вывод, что из массива ComposableReplacer<?, ?>.

Можно ли это сделать в java (и все еще иметь тип безопасности)?

EDIT Чтобы быть ясно, что проблема объявить метод, который принимает массив ComposableReplacer и штабели/цепей их, с типобезопасностью.

+1

Используйте шаблон оформления Decorator для создания реализаций Replacer: http://en.wikipedia.org/wiki/Decorator_design_pattern –

+2

Вы не сможете этого сделать, каждый «Replacer» должен либо взять метод в конструкторе, либо возможно, есть метод 'и' - таким образом вы можете безопасно выводить типы возврата. Что-то вроде шаблона «Цепь ответственности» должно работать. –

+0

@ BorisPavlović Я попробовал шаблон декоратора, но та же проблема возникает - нет способа сообщить компилятору типы в массиве 'ComposableReplacer': [здесь ссылка на реализацию] (http://pastebin.com/ReRSwTuA) – goncalopp

ответ

3

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

// won’t work 
public static <I, O> ComposableReplacer<I, O> compose(
    ComposableReplacer<I, O> rs1, ComposableReplacer<I, O> rs2) { 
    rs1.child=rs2; 
    return rs1; 
} 

Этот код еще не компилируется, как rs1.child требует ComposableReplacer<O,?> вместо ComposableReplacer<I,O> если вы это исправить, ваш метод становится

public static <I, O> ComposableReplacer<I, O> compose(
    ComposableReplacer<I, O> rs1, ComposableReplacer<O,?> rs2) { 
    rs1.child=rs2; 
    return rs1; 
} 

Теперь он работает, но два параметра имеют другой тип. Если у Java были массивы с типом безопасности, они должны были помещать одновременно ComposableReplacer<I, O> и ComposableReplacer<O,?>. (Если вы не применять O и I быть такой же.)

Чтобы проиллюстрировать его дальше, вот метод для трех параметров:

public static <I, O, X> ComposableReplacer<I, O> compose(
    ComposableReplacer<I, O> rs1, ComposableReplacer<O,X> rs2, 
    ComposableReplacer<X, ?> rs3) { 
    rs1.child=rs2; 
    rs2.child=rs3; 
    return rs1; 
} 

Здесь вы видите, что каждый аргумент имеет другой тип, и вам нужно дополнительный параметр типа, поэтому использование «типа безопасного массива» (см. java.util.List) не может обеспечить этого. Самое простое решение - сохранить метод двух аргументов и позволить вызывающему вызывающему его несколько раз. Или n-arg, если вы знаете, что использование аргументов n args потребуется очень часто.

0

Я бы somethign, как, что для достижения цели

Композиция может быть сделано:

  • если внутренний и внешний разные: передача ребенка заменителя родителю Заменитель
  • если внутренняя и Внешние равны:

    1. ssing child replacer to parent replacer,
    2. с использованием статического метода, который устанавливает дочерние элементы по мере необходимости.

.

abstract class Replacer<Outer, Inner> { 

    private Replacer<Inner, ?> child; 

    protected abstract Inner decompose(Outer something); 

    protected abstract Outer compose(Inner something); 

    protected abstract Inner inner_replace(Inner something); 

    public Replacer(Replacer<Inner, ?> child) { 
     this.child = child; 
    } 

    public Outer replace(Outer something) { 
     Inner s = decompose(something); 
     s = inner_replace(s); 
     if (child != null) 
      s = child.replace(s); 
     return compose(s); 
    } 

    public void setChild(Replacer<Inner, ?> child) { 
     this.child = child; 
    } 

    @SafeVarargs 
    public static <T> Replacer<T, T> compose(Replacer<T, T>... replacers) { 
     if (replacers.length == 0) 
      return new DummyReplacer<>(); 
     else { 
      Replacer<T, T> current = replacers[0]; 
      for (int i = 1; i < replacers.length; ++i) { 
       current.setChild(replacers[i]); 
       current = replacers[i]; 
      } 
      return replacers[0]; 
     } 
    } 
} 

class DummyReplacer<Outer> extends Replacer<Outer, Outer> { 

    public DummyReplacer(Replacer<Outer, ?> child) { 
     super(child); 
    } 

    public DummyReplacer() { 
     super(null); 
    } 

    @Override 
    protected Outer decompose(Outer something) { 
     return something; 
    } 

    @Override 
    protected Outer compose(Outer something) { 
     return something; 
    } 

    @Override 
    protected Outer inner_replace(Outer something) { 
     return something; 
    } 

} 

class Multiply extends Replacer<Integer, Integer> { 

    private int factor; 

    public Multiply(int factor, Replacer<Integer, ?> child) { 
     super(child); 
     this.factor = factor; 
    } 

    public Multiply(int factor) { 
     super(null); 
     this.factor = factor; 
    } 

    @Override 
    protected Integer decompose(Integer something) { 
     return something; 
    } 

    @Override 
    protected Integer compose(Integer something) { 
     return something; 
    } 

    @Override 
    protected Integer inner_replace(Integer something) { 
     return something * factor; 
    } 

} 

class Negate extends Replacer<String, Integer> { 
    public Negate(Replacer<Integer, ?> child) { 
     super(child); 
    } 

    public Negate() { 
     super(null); 
    } 

    @Override 
    protected Integer inner_replace(Integer something) { 
     return -something; 
    } 

    @Override 
    protected Integer decompose(String something) { 
     return Integer.parseInt(something); 
    } 

    @Override 
    protected String compose(Integer something) { 
     return something.toString(); 
    } 
} 

class SharpToTildeExtract extends Replacer<String, String> { 
    public SharpToTildeExtract(Replacer<String, ?> child) { 
     super(child); 
    } 

    public SharpToTildeExtract() { 
     super(null); 
    } 

    @Override 
    protected String decompose(String something) { 
     return something.substring(1, something.length() - 1); 
    } 

    @Override 
    protected String compose(String something) { 
     return "~" + something + "~"; 
    } 

    @Override 
    protected String inner_replace(String something) { 
     return something; 
    } 
} 

class UpperCaseReplacer extends Replacer<String, String> { 

    public UpperCaseReplacer(Replacer<String, ?> child) { 
     super(child); 
    } 

    public UpperCaseReplacer() { 
     super(null); 
    } 

    @Override 
    protected String decompose(String something) { 
     return something; 
    } 

    @Override 
    protected String compose(String something) { 
     return something; 
    } 

    @Override 
    protected String inner_replace(String something) { 
     return something.toUpperCase(); 
    } 
} 

public class Main { 
    public static void main(String[] args) { 
     System.out.println(new SharpToTildeExtract().replace("#abc#")); 
     System.out.println(new SharpToTildeExtract(new UpperCaseReplacer()).replace("#abc#")); 
     System.out.println(Replacer.compose(new SharpToTildeExtract(), new UpperCaseReplacer()).replace("#abc#")); 

     System.out.println(new SharpToTildeExtract(new Negate(new Multiply(2))).replace("#5#")); 
    } 
} 
0

ComposableReplacer есть проблема.

Попробуйте это:

public abstract class Replacer< Outer, Inner > { 
    private static class DelegatingReplacer< Outer, Inner > { 
     private final Replacer< Outer, Inner > rplDelegate; 
     public DelegatingReplacer(Replacer< Outer, Inner > rplDelegate) { this.rplDelegate = rplDelegate; } 

     @Override protected Inner decompose(Outer something) { return rplDelegate.decompose(something); } 
     @Override protected Outer compose(Inner something) { return rplDelegate.compose(something); } 
     @Override protected Inner inner_replace(Inner something) { return rplDelegate.inner_replace(something); } 
    } 
    protected abstract Inner decompose(Outer something); 
    protected abstract Outer compose(Inner something); 
    protected abstract Inner inner_replace(Inner something); 
    public final Outer replace(Outer something) { 
     return compose(inner_replace(decompose(something))); 
    } 
    public <Innerer> Replacer< Outer, Inner > chain(final Replacer< Inner, Innerer > rplChained) { 
     return new DelegatingReplacer< Outer, Inner >(this) { 
      @Override protected inner_replace(Inner something) { 
       return rplChained.replace(super.inner_replace(something)); 
      } 
     } 
    } 
} 

Теперь вы можете сделать

r1.chain(r2.chain(r3)).replace(foo); // for r1< O, I >, r2< I, J >, r3< J, K > 
r1.chain(r2).chain(r3).replace(foo); // for r1< O, I >, r2< I, J >, r3< I, K > 

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

Единственная причина, по которой требуется метод на основе массива, заключается в том, что во время выполнения были определены заменители (и по расширению их типов). Таким образом, ваш вопрос (как гарантировать безопасность во время компиляции типа массива с типами, определенными во время выполнения), кажется, не имеет смысла.