2011-12-30 31 views
5

Tôi đã đấu tranh với vấn đề sau. Tôi có một loạt các đối tượng hàm, mỗi đối tượng có các kiểu đầu vào và đầu ra riêng được xác định thông qua các đối số kiểu chung trong java. Tôi muốn sắp xếp chúng trong một chuỗi sao cho dữ liệu thô được nhập vào hàm đầu tiên, được chuyển thành kiểu đầu ra, là kiểu đầu vào của đối tượng tiếp theo, v.v. tất nhiên điều này sẽ là tầm thường đối với mã cứng, nhưng tôi muốn có mã được cắm vào các đối tượng hàm mới. nếu tôi chỉ bỏ qua đối số kiểu (chỉ loại đầu ra cuối cùng), đây là cách điều trông:Java Generics: kết hợp cùng nhau đối tượng hàm chung

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

đây một iterator trên các dữ liệu được truyền giữa các đối tượng chức năng, và bối cảnh nên bối cảnh. có cách nào để giữ loại ống cắm sau đây và vẫn duy trì an toàn loại?

chỉnh sửa: để rõ ràng, tôi có một loạt các đối tượng hàm, đường ống. mỗi loại đầu vào là một kiểu cụ thể và xuất ra một kiểu khác. (thực ra là một vòng lặp trên các kiểu này) chúng sẽ được xích lại với nhau, ví dụ: Pipe<A,B> -> Pipe<B,C> -> Pipe<C,D> -> ..., sao cho đầu ra của một đường ống là kiểu đầu vào cho đường ống tiếp theo. Ngoài ra còn có một nguồn ở đây mà kết quả đầu ra một vòng lặp của loại A, và một bồn rửa mà sẽ chấp nhận loại (đầu ra của ống quá khứ). điều này làm cho mọi thứ rõ ràng hơn? Câu hỏi đặt ra là, vì có sự phụ thuộc quan trọng vào tính tương thích của các kiểu đầu vào và đầu ra, có cách nào để đảm bảo điều này không?

Tôi bắt đầu nghĩ rằng khi chèn các đối tượng hàm vào đường ống có thể là thời gian tốt nhất để đảm bảo an toàn loại, nhưng tôi không chắc chắn cách thực hiện điều này.

chỉnh sửa 2: tôi có một phương pháp cộng cho các đối tượng chức năng hiện trông giống như dưới đây:

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

tôi muốn kiểm tra xem thông số Loại thứ nhất là giống như "kết thúc" của đường ống hiện tại, và ném một ngoại lệ nếu không? tôi không nghĩ rằng có một cách tốt để có được thời gian an toàn biên dịch ở đây. "cuối" của đường ống hiện tại có thể được đặt thành tham số loại thứ hai của đường ống đầu vào. Tôi không thể nghĩ làm thế nào để làm điều này với Generics, và đi qua các thông tin lớp học có vẻ khá ghê gớm.

+1

Loại tẩy xoá có thể làm cho cuộc sống của bạn khó khăn hơn cho các chức năng hoàn toàn chung. Trình biên dịch byte JVM loại bỏ kiểu từ cá thể của các lớp chung, do đó Danh sách trở thành Danh sách . –

+0

Bạn có thể giải thích yêu cầu này ít hơn không? Không thể tìm ra những gì bạn có và những gì bạn muốn có. –

+0

do đó, cách tiếp cận tốt nhất là để cố gắng đảm bảo an toàn loại khi chèn đối tượng hàm ống (how ??) và chỉ ngăn chặn cảnh báo về phương pháp trên? – downer

Trả lời

7

Đây là cách để thực hiện. Phương thức chạy không an toàn, nhưng với cách duy nhất để nối thêm một đường ống là làm theo cách an toàn, toàn bộ chuỗi là an toàn kiểu.

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

thực sự mát mẻ, và giống như tất cả các giải pháp tốt, nó rõ ràng một khi nó được biết đến :) – downer

+0

Cảm ơn câu trả lời của bạn :) Tôi tạo ra một [github project] (https://github.com/smartorigin/Dynamic-Workflow) dựa trên trả lời thêm chức năng không đồng bộ và thực thi song song. Hy vọng nó sẽ giúp ai đó. –

0

Đây là một cách khác để thực hiện: cách này cho phép bước chuyển đổi kết quả trong danh sách. Ví dụ, một phép biến đổi có thể tách một chuỗi thành nhiều phần tử. Hơn nữa, nó cho phép mã xử lý ngoại lệ phổ biến nếu việc chuyển đổi bất kỳ giá trị nào tạo ra một ngoại lệ. Nó cũng cho phép sử dụng một danh sách rỗng như một giá trị trả về thay vì một giá trị null không rõ ràng mà phải được kiểm tra để tránh NullPointerException. Vấn đề chính với vấn đề này là mỗi bước chuyển đổi hoàn toàn trước khi chuyển sang bước tiếp theo, điều này có thể không hiệu quả về bộ nhớ.

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

Rõ ràng, nếu bạn có thể sử dụng Java 8, thì bạn sẽ muốn sử dụng Luồng thay thế. Hoặc, giống như RxJava. – Shannon

Các vấn đề liên quan