我对访问者模式有点不友好,但我有一个需要访问者实现的任务(如果我想避免“instanceof”检查)。
我有一个类,它是几个gwt元素的包装:Label,Panel,Widget(可以是复选框,列表框,文本框等)。我使用数组作为UI的相似部分的集合。例如。标签+复选框,标签+文本框;标签+按钮等。
某些元素以不同的方式构建(另一个类的一部分派生自,例如,Panel)。因此,我有两个相同的构造函数,但在一个地方使用重载方法。我可以合并这些构造函数并使用上面提到的方法中的“instanceof”检查元素。但我不喜欢这个解决方案,并希望使用访问者模式替换它。说实话,我不知道怎么做,希望你帮忙。
以下是我的例子:
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);
}
}
像这样的东西(我试图使它最大化抽象,实际上它更难)。
所以我有一个使用“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);
}
}
我希望从“instanceof”中保存并只保留一个构造函数,如果可能的话,甚至可以使用一个没有重载版本的init方法。 提前感谢您的建议和帮助。
P.S>我再说一遍,上面的类是捏造的,看起来像是一些误解,尤其是这个String标签:)
答案 0 :(得分:3)
IMO,你现有的解决方案,有两个构造函数,很好。
您可以使用策略模式,让构造函数获取某个PanelProvider
接口的实例而不是Object。该接口将具有以下方法:
Panel createPanel(Widget widget);
。客户端会将StringPanelProvider
的实例或ConstructedClassPanelProvider
的实例传递给构造函数。因此,您的构造函数将如下所示:
public MyWidgets(PanelProvider panelProvider, Widget widget) {
this.widget = widget;
this.panel = panelProvider.createPanel(widget);
}
StringPanelProvider实现看起来像
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);
}
}
ConstructedClassPanelProvider看起来一样。
如果你真的想使用访客模式,那么你必须稍微修改一下:
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);
}
});
}
但这对我来说似乎是过度工程。
答案 1 :(得分:2)
使用visitor pattern的一个实现如下:
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);
}
}
此解决方案与JB Nizet的解决方案非常相似。此实现的ConstructorArgVisitor
和JB Nizet的Visitor
接口之间的区别在于方法名称。 visit
方法在ConstructorArgVisitor
中重载,而在JB Nizet的Visitor
中,方法名称包含其中的类型(例如stringVisited
)。重载visit
方法更类似于visitor pattern on the Wikipedia page的示例。
我同意JB Nizet的说法,使用访客模式可能有点过分;但是,如果您使用JB {Nizet建议的PanelProvider
,除非您提前知道参数是String
或ConstructedClass
,否则您可能仍需要执行instanceof
检查,你试图避免。
现在这是我个人的偏好,所以如果你愿意,你可以忽略:尽量不要像Misko Hevery在“Flaw: Constructor does Real Work”中推荐的那样在构造函数中工作。例如,您可以将构造逻辑移动到工厂中。以下使用上述访客模式的修改版本:
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);
}
我也看到你在构建过程中调用静态方法。尽管在很多代码库中,GUI很难测试,但您可能希望阅读“Flaw: Brittle Global State & Singletons”和“Guide: Writing Testable Code”。