2012-01-10 31 views
11

Tôi refactoring một số mã trong một dự án tôi đang làm việc trên và tôi chạy vào một lớn if/else if tuân theo định dạng:Refactoring trong Java, thay thế cho lượng lớn lệnh if

if (changer instanceof AppleChanger) 
{ 
    panel = new ApplePanel(); 
} 
else if (changer instanceof OrangeChanger) 
{ 
    panel = new OrangePanel(); 
} 

Bây giờ xung đầu tiên của tôi là tái cấu trúc nó bằng cách sử dụng đa hình để nó có dạng như

panel = changer.getChangerPanel(); 

Tuy nhiên, không may là gói lớp không có quyền truy cập vào gói bảng điều khiển.

xung tiếp theo của tôi là tạo ra một lớp PanelChooser với một phương pháp quá tải:

PanelChooser.getPanel(changer); 

//Overloaded Method 
public Panel getPanel(OrangeChanger changer) 
{ 
    Panel orangePanel = new OrangePanel(); 
    return orangePanel; 
} 
public Panel getPanel(AppleChanger changer) 
{ 
    Panel applePanel = new ApplePanel(); 
    return applePanel; 
} 

Đây có phải là một giải pháp tốt hoặc là có một cách tốt hơn để giải quyết này?

+1

tôi nghi ngờ điều thứ hai sẽ không làm việc bởi vì bạn sẽ phải bỏ một cách rõ ràng sự thay đổi của trong vào một lớp con cho java để xác định phương thức gọi. bạn có thể sử dụng một lớp đăng ký/bản đồ (để thay đổi bản đồ thành các bảng và tìm kiếm với thể hiện được thông qua) có lẽ. – aishwarya

+4

Tại sao tên lớp học của bạn bắt đầu bằng chữ thường? –

+0

@U Mad Cảm ơn, là lỗi đánh máy của tôi, đã sửa – FooBar

Trả lời

13

Vấn đề cơ bản 'ở đây là bạn có phân cấp lớp song song. Bạn sẽ không thể thay thế câu lệnh if nếu không có một số phép tái cấu trúc khá nặng nề. Một số đề xuất là on c2 wiki.

Điều tốt nhất bạn có thể làm và có thể là giải pháp hoàn hảo, là di chuyển câu lệnh if thành lớp 'nhà máy' và đảm bảo không được sao chép ở bất kỳ nơi nào khác.

+1

+1 để nhận dạng phân cấp lớp song song. Chắc chắn là một trường hợp cho các nhà máy. – earcam

+1

+1 Một mẫu nhà máy là giải pháp tốt nhất, nó cung cấp cho bạn một số tính linh hoạt bổ sung (ví dụ cho phép ánh xạ nhiều người từ các bộ đổi thành bảng), tách logic khỏi lớp thực tế và đặt "cấu hình" tất cả trong một địa điểm. – Viruzzo

+0

Điều này có vẻ như ý tưởng tốt nhất hiện nay, tôi nghĩ rằng việc tái cấu trúc ở mức độ cần thiết vượt quá trình độ chuyên môn của tôi và sử dụng một lớp nhà máy với một lưu ý về khả năng trong tương lai sẽ thỏa mãn mức hiện tại của tôi. – FooBar

1

Có lẽ bạn có thể làm:

public Panel getPanel(Changer changer) 
{ 
    String changerClassName = changer.class.getName(); 
    String panelClassName = changerClassName.replaceFirst("Changer", "Panel"); 
    Panel panel = (Panel) Class.forName(panelClassName).newInstance(); 
    return panel; 
} 

tôi không có chương trình trong Java, nhưng đó là những gì tôi sẽ cố gắng nếu điều này là trong C#. Tôi cũng không biết nếu điều này sẽ làm việc với các gói của bạn.

Chúc may mắn!

+1

Ok, nhưng điều này đòi hỏi phải có quy ước đặt tên cho các lớp học của bạn; phải có một 'xyzPanel' cho mỗi lớp' xyzChanger'. Và nó không thực sự an toàn, bởi vì có một dàn diễn viên trong 'Bảng điều khiển' ở đó. – Jesper

+2

IMHO, tốt hơn là tránh các thao tác như vậy với tên lớp, khi đó không phải là lựa chọn duy nhất. Khó duy trì, ví dụ: thay đổi tên lớp sẽ không gây ra bất kỳ lỗi biên dịch nào. – Wizart

+1

Vâng, đây sẽ là quy ước đặt tên. Công ước được cho là để tăng tốc độ phát triển. Tuy nhiên, 'getPanel' có thể ghi đè, trong trường hợp bạn muốn thay đổi quy ước cho một số loại' Changer'. Đây sẽ là mẫu thiết kế nhà máy trừu tượng, trong trường hợp bạn đang tự hỏi. –

0

Tôi không thấy đủ mã và thiết kế hiện tại. Vì vậy, có lẽ, trước hết tôi sẽ cố gắng để di chuyển mã với bảng điều khiển instantiation đến cùng một nơi Changer dụ được tạo ra. Bởi vì việc chọn một Panel là quyết định tương tự như việc chọn một Changer.

Nếu chọn một Changer được chọn động, bạn chỉ có thể tạo các bảng này và sau đó hiển thị/ẩn chúng cho phù hợp.

+0

Trình đổi được gán bằng cách sử dụng một cuộc gọi đến máy chủ, máy chủ không có quyền truy cập vào mã bảng nên tiếc là đó không phải là một tùy chọn. – FooBar

4

Tôi nghĩ rằng tốt của nó mà xung đầu tiên của bạn đã không hoạt động :) Nếu không bạn sẽ vài mã bạn đổi (mà phải là một cái gì đó về logic) để mã UI (bảng điều khiển) và sai của nó.

Bây giờ tôi có thể cung cấp cho bạn những giải pháp sau đây:

tạo PanelCreator giao diện với phương pháp Bảng điều chỉnh createPanel như thế này:

interface PanelCreator { 
    Panel createPanel(); 
} 

Bây giờ, cung cấp 2 triển khai:

public class OrangePanelCreator implements PanelCreator{ 
    Panel createPanel() { 
     return new OrangePanel(); 
    } 
} 

public class ApplePanelCreator implements PanelCreator { 

    Panel createPanel() { 
     return new ApplePanel(); 
    } 
} 

Và bây giờ đến phần thú vị:

Tạo bản đồ, PanelCr eator> này sẽ đóng vai trò như một danh bạ về tấm của bạn:

Map<Class<Changer>, PanelCreator> registry = new HashMap<>; 
registry.put(OrangeChanger.class, new OrangePanelCreator()); 
registry.put(AppleChanger.class, new ApplePanelCreator()); 

Và trong mã của bạn bây giờ bạn có thể làm những điều sau đây:

panel = registry.get(changer.getClass()).createPanel(); 

Tôi nghĩ rằng nó sẽ được thêm thanh lịch kể từ khi bạn có thể dễ dàng thay đổi triển khai của người tạo cho người đổi.

Hy vọng điều này sẽ giúp

+0

Giải pháp nhanh chóng tốt, nhưng giải pháp này sẽ không hoạt động nếu ai đó thêm lớp con, ví dụ: OrangeChanger. – artbristol

+0

bạn rằng trong trường hợp này đăng ký nên được cập nhật với các cuộc gọi "thêm"? –

+0

Bạn có thể làm điều đó, nhưng bạn đang phá vỡ Nguyên tắc thay thế Liskov. Không thực sự có lợi thế nào so với câu lệnh if, về các dòng mã được sử dụng để đạt được kết quả tương tự. – artbristol

0

Giải pháp của bạn sẽ không hoạt động, bởi vì Java chọn phương pháp dựa trên kiểu kết hợp (có thể đây là Changer). Bạn có thể sử dụng Map<Class<? extends Changer>, Panel> (hoặc Map<Class<? extends Changer>, Class<? extens Panel>> nếu bạn cần tạo các phiên bản mới mỗi lần). Giải pháp này yêu cầu thêm công việc nếu bạn cần điều này để làm việc cho - chưa biết - các lớp con của ví dụ OrangeChanger.

ví dụ cho một trường hợp duy nhất cho mỗi Changer lớp con

changerToPanel.get(changer.getClass()); 

hoặc nếu bạn cần các trường hợp mới:

changerToPanelClass.get(changer.getClass()).newInstance(); 

Các tùy chọn khác sẽ là để đi cho linh cảm ban đầu của bạn, và làm cho Changer biết về Panel.

0

Hãy xem các mẫu FactoryAbstract Factory.

Nhà máy Nhà máy Mẫu là hình mẫu sáng tạo như được sử dụng để kiểm soát việc khởi tạo lớp học. Mẫu nhà máy được sử dụng để thay thế các nhà xây dựng lớp, tóm tắt quá trình tạo đối tượng để loại đối tượng được khởi tạo có thể được xác định trong thời gian chạy.

Nhà máy trừu tượng Mẫu là mẫu hình sáng tạo, vì nó được sử dụng để kiểm soát việc khởi tạo lớp. Mẫu nhà máy trừu tượng được sử dụng để cung cấp cho khách hàng một tập hợp các đối tượng liên quan hoặc phụ thuộc. Các đối tượng của các đối tượng được tạo ra bởi nhà máy được xác định tại thời gian chạy theo việc lựa chọn các lớp nhà máy bê tông.

0

Tôi sẽ làm như sau:

Có giao diện PanelChooser với một phương thức trả về bảng điều khiển cho trình thay đổi.

Yêu cầu ClassBasedPanelChooser triển khai trả về bảng điều khiển khi Thay đổi triển khai một lớp nhất định và null khác. Lớp và bảng điều khiển được trả về sẽ được truyền vào trong hàm tạo. Có một thực thi khác CascadingPanelChooser trong đó có một danh sách các PanelChoosers trong các đối số constructor và gọi phương thức của nó yêu cầu mỗi PanelChooser cung cấp một bảng cho đến khi nó nhận được một bảng không rỗng, sau đó nó trả về bảng đó.

0

Không sử dụng instanceof. Why polymorphism fails Nơi duy nhất để sử dụng instanceof nằm trong phương thức tương đương.

Để trả lời câu hỏi của bạn. Theo dõi số link này. Khoản tín dụng cho Cowanjordao.

  • Sử dụng Reflection.
public final class Handler { 
    public static void handle(Object o) { 
    for (Method handler : Handler.class.getMethods()) { 
     if (handler.getName().equals("getPanel") && 
      handler.getParameterTypes()[0] == o.getClass()) { 
     try { 
      handler.invoke(null, o); 
      return; 
     } catch (Exception e) { 
      throw new RuntimeException(e); 
     } 
     } 
    } 
    throw new RuntimeException("Can't handle"); 
    } 
public static void handle(Apple num) { /* ... */ } 
    public static void handle(Orange num) { /* ... */ } 
  • Chuỗi Trách nhiệm
public abstract class Changer{ 
    private Changer next; 

    public final boolean handle(Object o) { 
     boolean handled = doHandle(o); 
     if (handled) { return true; } 
     else if (next == null) { return false; } 
     else { return next.handle(o); } 
    } 

    public void setNext(Changer next) { this.next = next; } 

    protected abstract boolean doHandle(Object o); 
} 

public class AppleHandler extends Changer{ 
    @Override 
    protected boolean doHandle(Object o) { 
     if (!o instanceof Apple) { 
     return false; 
     } 
     OrangeHandler.handle((Orange) o); 
     return true; 
    } 
} 
2

Nếu có nhiều hơn một trong những này if/else xây dựng trong các mã dependending vào loại thể hiện của một Changer, bạn có thể sử dụng mẫu người truy cập như thế này:

public interface ChangerVisitor { 
    void visit(OrangeChanger changer); 
    void visit(AppleChanger changer); 
    ... 
} 

public class ChangerVisitorEnabler<V extends ChangerVisitor> { 
    public static <V extends ChangerVisitor> ChangerVisitorEnabler<V> enable(V) { 
    return new ChangerVisitorEnabler<V>(visitor); 
    } 

    private final V visitor; 

    private ChangerVisitorEnabler(V visitor) { 
    this.visitor = visitor; 
    } 

    public V visit(Charger changer) { 
    if (changer instanceof OrangeChanger) { 
     visitor.visit((OrangeChanger)changer); 
    } else if (changer instanceof AppleChanger) { 
     visitor.visit((AppleChanger)changer); 
    } else { 
     throw new IllegalArgumentException("Unsupported charger type: " + changer); 
    } 
    return visitor; 
    } 
} 

Bây giờ bạn có một loại mã kiểm tra đơn khối và một kiểu giao diện an toàn:

public PanelChooser implements ChangerVisitor { 

    public static Panel choosePanel(Changer changer) { 
    return ChangerVisitorEnabler.enable(new PanelChooser()).visit(changer).panel; 
    } 

    private Panel panel; 

    private PanelChooser() { 
    } 

    void visit(OrangeChanger changer) { 
    panel = orangePanel(); 
    } 

    void visit(AppleChanger changer) { 
    panel = applePanel(); 
    } 

} 

việc sử dụng rất đơn giản:

panel = PanelChooser.choosePanel(chooser); 
+0

khách truy cập +1 cũng tốt. –

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