2011-12-30 16 views
5

Aşağıdaki sorunla uğraşıyordum. Her biri java'da jenerik tip argümanları ile tanımlanmış kendi giriş ve çıkış tiplerine sahip bir dizi fonksiyon nesnem var. Bunları bir zincir içinde düzenlemek istiyorum, böylece ham veri, bir sonraki nesnenin girdi türü olan çıkış türüne dönüştürülmüş, birinci işleve girilir, vb. Tabii ki bu, sabit kod için önemsiz olurdu, ancak kodun yeni işlev nesnelerine takılabilmesini istiyorum. ben sadece tip argümanları (sadece son çıkış tipi) dışarı bırakırsanız, bu görünüşünden: Burada Java Generics: birlikte zincirleme genel işlev nesnesi

public T process() { 
     Iterator<Context> it = source.provideData(); 
     for(Pipe pipe : pipeline) { 
      it = pipe.processIterator(it); 
     } 
     return sink.next(it); 
    } 

veriler üzerinde bir yineleyici fonksiyon nesneleri arasında geçirilir ve bağlam Bağlam olmalıdır

. Aşağıdaki türde bir borunun takılıp çıkarılabilmesi ve tip güvenliğinin korunmasının bir yolu var mı?

düzenleme: netlik için, bir dizi işlev nesnesi, borularım var. her biri belirli bir girdi olarak alır ve başka bir tip çıkarır. (aslında bu tipler üzerinde bir yineleyiciler), örneğin Pipe<A,B> -> Pipe<B,C> -> Pipe<C,D> -> ..., örneğin bir borunun çıktısı bir sonraki boru için giriş tipi olacak şekilde zincirlenecektir. Burada A tipi bir yineleyiciyi ve türünü (geçmiş borunun çıkışını) kabul edecek bir lavabo üreten bir kaynak da vardır. Bu işleri daha netleştirir mi? Soru şu ki, girdi ve çıktı türlerinin uyumluluğuna kritik bir bağımlılık var, bunun için bir yol var mı?

Fonksiyon nesnelerinin boru hattına eklenmesi, tip güvenliğini sağlamak için en uygun zaman olabileceğini düşünmüyorum, ancak bunu nasıl yapacağımı bilmiyorum. Ben şu anda aşağıda benziyor fonksiyon nesneler için bir toplayıcı yöntemine sahip : düzenlemek 2

public void addPipe(Pipe<?,?> pipe) { 
    pipeline.add(pipe); 
} 

ilk tür parametresi "sonu" nin aynı olup olmadığını ben kontrol etmek istiyorum Geçerli boru ve değilse bir istisna atmak? Burada derleme güvenliği sağlamak için iyi bir yol olduğunu düşünmüyorum. Mevcut borunun "ucu" daha sonra giriş borusunun ikinci tip paramına ayarlanabilir. Bunu jeneriklerle nasıl yapacağımı düşünemiyorum ve sınıf bilgilerinin içinden geçmek oldukça çirkin görünüyor.

+1

Tür silme, yaşamınızı tamamen genel işlevler için zorlaştırabilir. JVM bayt derleyici türünü genel sınıflar örneğinden kaldırır, bu nedenle Liste Liste olur. –

+0

Gereksinimi biraz daha açıklayabilir misiniz? Ne sahip olduğunuzu ve neye sahip olmak istediğinizi çözemiyorum. –

+0

, bu nedenle, en iyi yaklaşım, boru fonksiyon nesnesini yerleştirirken (nasıl?) Tip güvenliğinden emin olmak ve sadece yukarıdaki yöntemle ilgili uyarıları bastırmaktır? – downer

cevap

7

İşte bunu yapmanın bir yolu. Çalıştırma yöntemi güvenilir değildir, ancak bir boruyu eklemenin tek yolunun bunu tür güvenli bir şekilde yapmak olduğu göz önüne alındığında, tüm zincir tipi güvenlidir.

public class Chain<S, T> { 
    private List<Pipe<?, ?>> pipes; 

    private Chain() { 
    } 

    public static <K, L> Chain<K, L> start(Pipe<K, L> pipe) { 
     Chain<K, L> chain = new Chain<K, L>(); 
     chain.pipes = Collections.<Pipe<?, ?>>singletonList(pipe);; 
     return chain; 
    } 

    public <V> Chain<S, V> append(Pipe<T, V> pipe) { 
     Chain<S, V> chain = new Chain<S, V>(); 
     chain.pipes = new ArrayList<Pipe<?, ?>>(pipes); 
     chain.pipes.add(pipe); 
     return chain; 
    } 

    @SuppressWarnings({ "rawtypes", "unchecked" }) 
    public T run(S s) { 
     Object source = s; 
     Object target = null; 
     for (Pipe p : pipes) { 
      target = p.transform(source); 
      source = target; 
     } 
     return (T) target; 
    } 

    public static void main(String[] args) { 
     Pipe<String, Integer> pipe1 = new Pipe<String, Integer>() { 
      @Override 
      public Integer transform(String s) { 
       return Integer.valueOf(s); 
      } 
     }; 
     Pipe<Integer, Long> pipe2 = new Pipe<Integer, Long>() { 
      @Override 
      public Long transform(Integer s) { 
       return s.longValue(); 
      } 
     }; 
     Pipe<Long, BigInteger> pipe3 = new Pipe<Long, BigInteger>() { 
      @Override 
      public BigInteger transform(Long s) { 
       return new BigInteger(s.toString()); 
      } 
     }; 
     Chain<String, BigInteger> chain = Chain.start(pipe1).append(pipe2).append(pipe3); 
     BigInteger result = chain.run("12"); 
     System.out.println(result); 
    } 
} 
+0

gerçekten harika, ve tüm iyi çözümler gibi, bir kez bilinir belli oluyor :) – downer

+0

Cevabınız için teşekkürler :) Ben bir [github projesi] (https://github.com/smartorigin/Dynamic-Workflow) oluşturmak uyumsuzluk işlevselliği ve paralel yürütme ekleme yanıtı. Umarım birisine yardım eder. –

0

İşte bunu yapmanın başka bir yolu: bu yol, bir listeyle sonuçlanacak bir dönüştürme adımına izin verir. Örneğin, bir dönüşüm bir dizeyi çoklu alt dizelere bölebilir. Ayrıca, herhangi bir değerin dönüştürülmesinin bir istisna oluşturması durumunda ortak istisna işlem koduna izin verir. Ayrıca boş bir Listenin NullPointerException'dan kaçınmak için test edilmesi gereken belirsiz bir boş değer yerine dönüş değeri olarak kullanılmasına izin verir. Bununla ilgili temel problem, bir sonraki adıma geçmeden önce, bellek verimli olamayabilen, tüm dönüşüm adımını bütünüyle gerçekleştirmesidir.

public class Chain<IN, MEDIAL, OUT> { 
    private final Chain<IN, ?, MEDIAL> head; 
    private final Transformer<MEDIAL, OUT> tail; 

    public static <I, O> Chain<I, I, O> makeHead(@Nonnull Transformer<I, O> tail) { 
     return new Chain<>(null, tail); 
    } 

    public static <I, M, O> Chain<I, M, O> append(@Nonnull Chain<I, ?, M> head, @Nonnull Transformer<M, O> tail) { 
     return new Chain<>(head, tail); 
    } 

    private Chain(@Nullable Chain<IN, ?, MEDIAL> head, @Nonnull Transformer<MEDIAL, OUT> tail) { 
     this.head = head; 
     this.tail = tail; 
    } 

    public List<OUT> run(List<IN> input) { 
     List<OUT> allResults = new ArrayList<>(); 

     List<MEDIAL> headResult; 
     if (head == null) { 
      headResult = (List<MEDIAL>) input; 
     } else { 
      headResult = head.run(input); 
     } 

     for (MEDIAL in : headResult) { 
      // try/catch here 
      allResults.addAll(tail.transform(in)); 
     } 

     return allResults; 
    } 

    public static void main(String[] args) { 

     Transformer<String, Integer> pipe1 = new Transformer<String, Integer>() { 
      @Override 
      public List<Integer> transform(String s) { 
       return Collections.singletonList(Integer.valueOf(s) * 3); 
      } 
     }; 
     Transformer<Integer, Long> pipe2 = new Transformer<Integer, Long>() { 
      @Override 
      public List<Long> transform(Integer s) { 
       return Collections.singletonList(s.longValue() * 5); 
      } 
     }; 
     Transformer<Long, BigInteger> pipe3 = new Transformer<Long, BigInteger>() { 
      @Override 
      public List<BigInteger> transform(Long s) { 
       return Collections.singletonList(new BigInteger(String.valueOf(s * 7))); 
      } 
     }; 
     Chain<String, ?, Integer> chain1 = Chain.makeHead(pipe1); 
     Chain<String, Integer, Long> chain2 = Chain.append(chain1, pipe2); 
     Chain<String, Long, BigInteger> chain3 = Chain.append(chain2, pipe3); 
     List<BigInteger> result = chain3.run(Collections.singletonList("1")); 
     System.out.println(result); 
    } 
} 
+0

Açıkçası, Java 8'i kullanabiliyorsanız, bunun yerine Akışları kullanmak istersiniz. Ya da RxJava gibi bir şey. – Shannon