2011-01-26 50 views
7

Tôi đang viết một phương pháp trong Java:DRY: Giảm thiểu đang lặp đi lặp lại trong Java

List<Foo> computeFooList(/* arguments */) 
{ 
    /* snip */ 
} 

Tôi muốn viết một phương pháp thứ hai với chính xác cùng một logic, nhưng một kiểu trả về khác nhau:

List<String> computeStringList(/* same arguments */) 
{ 
    /* snip */ 
} 

Tôi đang cố gắng tìm ra cách không hack để giảm thiểu số lượng mã lặp lại giữa hai phương pháp. Sự khác biệt chỉ logic giữa hai là, khi thêm một đối tượng vào danh sách đó là quay trở lại, phương pháp đầu tiên thêm acutal Foo:

List<Foo> computeFooList(/* arguments */) 
{ 
    List<Foo> toReturn = ... 
    ... 
    for (Foo foo : /* some other list of Foo */) 
    { 
     if (/* some condition */) 
     { 
      toReturn.add(foo); 
     } 
    } 
    ... 
    return toReturn; 
} 

và thứ hai cho biết thêm một đại diện String của Foo:

List<String> computeStringList(/* same arguments */) 
{ 
    List<String> toReturn = ... 
    ... 
    for (Foo foo : /* some other list of Foo */) 
    { 
     if (/* some condition */) 
     { 
      toReturn.add(foo.toString()); 
     } 
    } 
    ... 
} 

Trong thực tế, nó không hoàn toàn rằng đơn giản. Tôi không muốn thêm Foo vào toReturn trừ khi tôi hoàn toàn chắc chắn rằng nó thuộc về đó. Kết quả là, quyết định đó được thực hiện theo số foo bằng cách sử dụng các hàm trợ giúp. Với hai phiên bản khác nhau của các phương thức, tôi cũng cần các phiên bản khác nhau của các hàm trợ giúp - cuối cùng, tôi sẽ viết hai tập các phương thức giống hệt nhau, nhưng với một kiểu chung chung nhỏ.


Tôi có thể viết một phương pháp duy nhất chứa tất cả các logic ra quyết định, nhưng có thể tạo ra hoặc là một List<Foo> hoặc một List<String>? Có thể làm điều này mà không cần sử dụng các loại thô List (thực hành không tốt trong đất generics!) Hoặc ký tự đại diện List<?> loại? Tôi hình dung triển khai trông giống như thế này:

List<Foo> computeFooList(/* args */) 
{ 
    return computeEitherList(/* args */, Foo.class); 
} 

List<String> computeStringList(/* args */) 
{ 
    return computeEitherList(/* args */, String.class); 
} 

private List<???> computeEitherList(/* args */, Class<?> whichType) 
{ 
    /* snip */ 
} 

Có cách nào tốt đẹp, thanh lịch để làm việc này không? Tôi đã chơi xung quanh với các phương pháp chung, nhưng tôi không thể nhìn thấy một cách để làm điều này. Ngay cả mucking về với sự phản ánh đã không nhận được tôi bất cứ nơi nào (có lẽ tôi cần một cái gì đó như TypeToken? ... eww).

+0

Bạn có thể quan tâm vào trang web stackexchange mới này: http://codereview.stackexchange.com/ – Mchl

+0

@Mchl: ** Gah * * - bây giờ nó lên –

Trả lời

7

bạn không thể ra bên ngoài Logic chuyển đổi thành một chiến lược riêng biệt (chẳng hạn như Function<F, T> ổi của):

public <T> List<T> computeList(/* arguments */, Function<? super Foo, T> f) { 
    List<T> toReturn = ...  ...  
    for (Foo foo : /* some other list of Foo */) { 
     if (/* some condition */) { 
      toReturn.add(f.apply(foo)); 
     } 
    } 
    return toReturn; 
} 

computeFooList:

computeList(..., Functions.identity()); 

computeStringList:

computeList(..., Functions.toStringFunction()); 
+0

Điều này chắc chắn có vẻ đầy hứa hẹn. Hãy để tôi chơi xung quanh với điều này. –

+0

Rực rỡ! Cảm ơn bạn. Tôi cũng đã sử dụng ổi. Tôi cần đào sâu hơn vào nó. –

0

Tôi có một Giao diện "SearchFilter" và lớp tóm tắt "FilterAdapter" mà tôi sử dụng theo cách tương tự như thế này. Logic quyết định có thể được thực hiện một cách độc lập và theo cách tổng quát từ việc thực sự thêm những thứ vào danh sách trả về. Tôi sẽ kiểm tra từng Foo và nói "true" bao gồm nó hoặc "false" loại trừ nó.

public interface SearchFilter<T> 
{ 
    public boolean include(T item); 
    public Collection<T> apply(Collection<T> items); 
} 

Một bộ lọc có thể được áp dụng cho một bộ sưu tập hiện với phương pháp apply(), trở về một bộ sưu tập mới mà chỉ bao gồm các mục mong muốn.

newCollection = myfilter.apply(originalItems); 

Điều này có thể không hữu ích cho bạn, nhưng khái niệm include() sẽ hoạt động tốt để tránh lặp lại logic quyết định.

Bạn có thể có một FooFilter extends FilterAdapter<Foo> (Tôi cũng nhanh chóng những nặc danh trong dòng đôi khi) mà cung cấp một thực hiện include

public FooFilter extends FilterAdapter<Foo> 
{ 
    public boolean include(Foo item) 
    { 
     if (item.isInvalid()) return false; 
     // or any other complex logic you want 
     return item.hasGoodThings(); 
    } 
} 

Phương pháp apply() là hầu như luôn luôn chỉ là vòng lặp trong bộ sưu tập và thử nghiệm ban đầu include nên nó có cài đặt mặc định trong số FilterAdapter của tôi nhưng có thể bị ghi đè.

0

Đó là một chút xấu xí, nhưng tôi nghĩ rằng điều này có thể làm việc:

List<Foo> computeFooList(/* args */) { 
    return computeEitherList(/* args */, Foo.class); 
} 

List<String> computeStringList(/* args */) { 
    return computeEitherList(/* args */, String.class); 
} 

private <T> List<T> computeEitherList(/* args */, Class<T> whichType) { 
    List<T> rval = new ArrayList<T>(); 
    for (Foo foo : listOfFoo) { 
     // process foo 

     if (whichType.equals(Foo.class)) { 
      rval.add(whichType.cast(foo)); 
     } 
     else if (whichType.equals(String.class)) { 
      rval.add(whichType.cast(foo.toString())); 
     } 
     else { 
      throw new SomeException("Cannot compute list for type " + whichType); 
     } 
    } 
    return rval; 
} 
Các vấn đề liên quan