2012-05-12 23 views
5

Tôi hơi không thân thiện với mẫu Khách truy cập, nhưng tôi có một nhiệm vụ cần thực hiện Khách truy cập (nếu tôi muốn tránh kiểm tra "instanceof").Cách sử dụng mẫu Khách truy cập để thay thế "instanceof"

Tôi có một lớp là trình bao bọc cho một số phần tử gwt: Nhãn, Bảng điều khiển, Tiện ích con (có thể là hộp kiểm, hộp danh sách, hộp văn bản, v.v.). Tôi sử dụng một mảng như một bộ sưu tập giống như các phần của giao diện người dùng. Ví dụ. Nhãn + hộp kiểm, nhãn + hộp văn bản; Nhãn + nút, vv ..

Một số thành phần được tạo theo cách khác (một phần của một lớp khác bắt nguồn từ, ví dụ như Panel). Vì vậy, kết quả là tôi có hai nhà xây dựng giống nhau, nhưng ở một nơi quá tải phương pháp được sử dụng. Tôi có thể hợp nhất các hàm tạo này và kiểm tra phần tử bằng cách sử dụng "instanceof" bên trong phương thức được đề cập. Nhưng tôi không thích giải pháp này và muốn thay thế nó bằng cách sử dụng mẫu Visitor. Để nói sự thật, tôi không biết làm thế nào để làm điều đó và hy vọng bạn giúp đỡ.

Dưới đây là một ví dụ về những gì tôi có:

public class MyWidgets { 
    private String stringLabel; 
    private Widget widget; 
    private Panel panel; 

    public MyWidgets(String stringLabel, Widget widget) { 
     this.stringLabel = stringLabel; 
     this.widget = widget; 

     initPanel(stringLabel, widget); 
    } 

    public MyWidgets(ConstructedClass cs, Widget widget) { 
     this.widget = widget; 

     initPanel(cs, widget); 
    } 

    private initPanel(String label, Widget widget) { 
     panel = SomeStaticUtilityClass.initPanel(new Label(label), widget); 
    } 

    private initPanel(ConstructedClass cs, Widget widget) { 
     panel = SomeStaticUtilityClass(cs, widget); 
    } 
} 

Something như thế này (tôi đã cố gắng để làm cho nó tối đa trừu tượng, trong thực tế nó là khó khăn hơn).

Vì vậy, tôi có một giải pháp sử dụng "instanceof":

private initPanel(Object object, Widget widget) { 
    if(object instanceof String) { 
    panel = SomeStaticUtilityClass.initPanel(new Label(label), widget); 
    } 
    if(object instanceof ConstructedClass) { 
    panel = SomeStaticUtilityClass.initPanelFromObject(cs, widget); 
    } 
} 

Tôi muốn được cứu khỏi "instanceof" và để lại chỉ là một nhà xây dựng và thậm chí, nếu có thể, một phương pháp init mà không phiên bản quá tải của nó . Cảm ơn bạn đã đề xuất, trợ giúp.

Tái bút> Tôi nhắc lại, rằng lớp trên là bịa đặt, và trông giống như một số hiểu lầm đặc biệt là với String này nhãn :)

+0

Hmm .. có vẻ hơi lạ với dòng sau: "panel = SomeStaticUtilityClass (cs, widget);". Là SomeStaticUtilityClass một lớp học hoặc một phương pháp? :) – Javaguru

+0

Tôi đã chỉnh sửa ví dụ – Dragon

Trả lời

3

IMO, giải pháp hiện tại của bạn, với hai nhà thầu, là tốt.

Bạn có thể sử dụng mẫu chiến lược và yêu cầu hàm tạo của bạn lấy một phiên bản của một số giao diện PanelProvider thay vì Object. Giao diện này sẽ có phương thức sau:

Panel createPanel(Widget widget); 

. Khách hàng sẽ chuyển một phiên bản của StringPanelProvider hoặc một phiên bản của ConstructedClassPanelProvider cho nhà xây dựng. constructor của bạn do đó sẽ như thế nào:

public MyWidgets(PanelProvider panelProvider, Widget widget) { 
    this.widget = widget; 
    this.panel = panelProvider.createPanel(widget); 
} 

Và việc thực hiện StringPanelProvider sẽ trông như thế

public class StringPanelProvider implements PanelProvider { 

    private String s; 

    public StringPanelProvider(String s) { 
     this.s = s; 
    } 

    @Override 
    public Panel createPanel(Widget widget) { 
     return SomeStaticUtilityClass.initPanel(new Label(s), widget); 
    } 
} 

Các ConstructedClassPanelProvider sẽ giống nhau.

Nếu bạn thực sự muốn sử dụng mô hình của khách sau đó bạn sẽ phải thay đổi trên một chút:

public interface Visitable { 
    void accept(Visitor visitor); 
} 

public interface Visitor { 
    void stringVisited(String s); 
    void constructedClassVisited(ConstructedClass cs); 
} 

public class StringVisitable { 
    private String s; 

    public StringVisitable(String s) { 
     this.s = s; 
    } 

    void accept(Visitor visitor) { 
     visitor.stringVisited(s); 
    } 
} 

// similar for ConstructedClassVisitable 

public MyWidgets(Visitable visitable, final Widget widget) { 
    this.widget = widget; 
    visitable.accept(new Visitor() { 
     public void stringVisited(String s) { 
      panel = SomeStaticUtilityClass.initPanel(new Label(label), widget); 
     } 

     public void constructedClassVisited(ConstructedClass cs) { 
      panel = SomeStaticUtilityClass.initPanelFromObject(cs, widget); 
     } 
    }); 
} 

Nhưng điều này có vẻ như overengineering với tôi.

+0

Tôi nghĩ rằng mẫu chiến lược kết hợp với nhà máy sản xuất bảng điều khiển có vẻ phù hợp hơn so với việc sử dụng khách truy cập. – Javaguru

+0

Cả hai biến thể: của bạn và của creemama là tốt. Chúng hữu ích cho tôi. Cảm ơn rất nhiều. – Dragon

2

Một thực hiện bằng cách sử dụng visitor pattern là như sau:

public interface ConstructionArgVisitor { 
    void visit(LabelText text); 

    void visit(ConstructedClass clazz); 
} 

public interface ConstructionArg { 
    void accept(ConstructionArgVisitor visitor); 
} 

public class LabelText implements ConstructionArg { 
    private final String text; 

    public LabelText(String str) { 
     this.text = str; 
    } 

    @Override 
    public void accept(ConstructionArgVisitor visitor) { 
     visitor.visit(this); 
    } 

    public String getString() { 
     return this.text; 
    } 
} 

public class ConstructedClass implements ConstructionArg { 
    @Override 
    public void accept(ConstructionArgVisitor visitor) { 
     visitor.visit(this); 
    } 
} 

public class MyWidgets implements ConstructionArgVisitor { 
    private String stringLabel; 
    private Widget widget; 
    private Panel panel; 

    public MyWidgets(ConstructionArg constructionArg, Widget widget) { 
     this.widget = widget; 
     constructionArg.accept(this); 
    } 

    @Override 
    public void visit(LabelText labelText) { 
     this.stringLabel = labelText.getString(); 
     this.panel = SomeStaticUtilityClass.initPanel(new Label(labelText.getString()), this.widget); 
    } 

    @Override 
    public void visit(ConstructedClass clazz) { 
     this.panel = SomeStaticUtilityClass.initPanelFromObject(clazz, this.widget); 
    } 
} 

Giải pháp này rất giống với một JB Nizet của.Sự khác biệt giữa giao diện ConstructorArgVisitor và JB Nizet là Visitor của JB Nizet là tên phương thức. Phương pháp visit bị quá tải trong ConstructorArgVisitor, trong khi ở JB Nizet của Visitor, tên phương pháp chứa các loại trong họ (ví dụ, stringVisited). Quá tải phương thức visit gần giống với ví dụ về visitor pattern on the Wikipedia page.

Tôi đồng ý với JB Nizet rằng việc sử dụng mẫu khách truy cập có thể hơi quá tải; tuy nhiên, nếu bạn sử dụng PanelProvider như JB Nizet đề xuất, trừ khi bạn biết đối số là String hoặc ConstructedClass trước thời hạn, bạn vẫn có thể cần thực hiện kiểm tra instanceof mà bạn đang cố gắng tránh.

Bây giờ đây là sở thích cá nhân của tôi, vì vậy bạn có thể bỏ qua nếu bạn thích: Cố gắng không làm việc trong hàm tạo như Misko Hevery đề xuất trong "Flaw: Constructor does Real Work". Ví dụ, bạn có thể di chuyển logic xây dựng vào một nhà máy. Phần sau sử dụng phiên bản đã sửa đổi của mẫu khách truy cập ở trên:

public interface ConstructionArgVisitor<T> { 
    T visit(LabelText text); 

    T visit(ConstructedClass clazz); 
} 

public interface ConstructionArg { 
    <T> T accept(ConstructionArgVisitor<T> visitor); 
} 

public class LabelText implements ConstructionArg { 
    private final String text; 

    public LabelText(String str) { 
     this.text = str; 
    } 

    @Override 
    public <T> T accept(ConstructionArgVisitor<T> visitor) { 
     return visitor.visit(this); 
    } 

    public String getString() { 
     return this.text; 
    } 
} 

public class ConstructedClass implements ConstructionArg { 
    @Override 
    public <T> T accept(ConstructionArgVisitor<T> visitor) { 
     return visitor.visit(this); 
    } 
} 

public class MyWidgetsFactory implements ConstructionArgVisitor<MyWidgets> { 
    private final Widget widget; 

    public MyWidgetsFactory(Widget widget) { 
     this.widget = widget; 
    } 

    public MyWidgets createMyWidgets(ConstructionArg constructionArg) { 
     return constructionArg.accept(this); 
    } 

    @Override 
    public MyWidgets visit(LabelText text) { 
     return new MyWidgets(text.getString(), this.widget, SomeStaticUtilityClass.initPanel(
       new Label(text.getString()), this.widget)); 
    } 

    @Override 
    public MyWidgets visit(ConstructedClass clazz) { 
     return new MyWidgets(null, this.widget, SomeStaticUtilityClass.initPanelFromObject(clazz, this.widget)); 
    } 
} 

public class MyWidgets { 
    private final String stringLabel; 
    private final Widget widget; 
    private final Panel panel; 

    public MyWidgets(String stringLabel, Widget widget, Panel panel) { 
     this.stringLabel = stringLabel; 
     this.widget = widget; 
     this.panel = panel; 
    } 
} 

public static void main(String[] args) { 
    final Widget widget = ...; 
    final MyWidgetsFactory factory = new MyWidgetsFactory(widget); 

    // create MyWidgets from label text 
    final String str = ...; 
    final MyWidgets labelWidget = factory.createMyWidgets(new LabelText(str)); 

    // create MyWidgets from constructed class 
    final ConstructedClass clazz = ...; 
    final MyWidgets constructedClassWidget = factory.createMyWidgets(clazz); 
} 

Tôi cũng thấy rằng bạn đang gọi phương thức tĩnh trong khi xây dựng. Mặc dù trong rất nhiều GUI mã vạch nổi tiếng là khó kiểm tra, bạn có thể muốn đọc "Flaw: Brittle Global State & Singletons" và "Guide: Writing Testable Code".

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