我正在重构我正在处理的项目中的一些代码,并且遇到了一个大的if / else if语句,该语句遵循以下格式:
if (changer instanceof AppleChanger)
{
panel = new ApplePanel();
}
else if (changer instanceof OrangeChanger)
{
panel = new OrangePanel();
}
现在我的第一个冲动是使用多态来重构它,使其看起来像
panel = changer.getChangerPanel();
但遗憾的是,类包无法访问面板包。
我的下一个冲动是创建一个带有重载方法的PanelChooser类:
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;
}
这是一个很好的解决方案还是有更好的解决方法?
答案 0 :(得分:13)
这里的基本“问题”是你有并行的类层次结构。如果没有一些相当重的重构,你将无法替换if语句。一些建议是on c2 wiki。
你能做的最好的,也许是一个非常好的解决方案,就是将if语句移到'工厂'类中,并确保它在其他任何地方都不会重复。
答案 1 :(得分:4)
我认为你的第一次冲动不起作用很好:)否则你会把你的转换器代码(应该是关于逻辑的东西)与UI代码(面板)和错误结合起来。
现在我可以为您提供以下解决方案:
使用方法Panel createPanel创建一个接口PanelCreator,如下所示:
interface PanelCreator {
Panel createPanel();
}
现在,提供2个实现:
public class OrangePanelCreator implements PanelCreator{
Panel createPanel() {
return new OrangePanel();
}
}
public class ApplePanelCreator implements PanelCreator {
Panel createPanel() {
return new ApplePanel();
}
}
现在来到有趣的部分:
创建地图,PanelCreator>这将像您的面板的注册表一样:
Map<Class<Changer>, PanelCreator> registry = new HashMap<>;
registry.put(OrangeChanger.class, new OrangePanelCreator());
registry.put(AppleChanger.class, new ApplePanelCreator());
现在,您可以在代码中执行以下操作:
panel = registry.get(changer.getClass()).createPanel();
我认为它会更优雅,因为您可以轻松更改给定更换器的创建者的实现。
希望这有帮助
答案 2 :(得分:2)
如果代码中有多个if / else结构依赖于Changer的实例类型,则可以使用这样的访问者模式:
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;
}
}
现在您有一个类型检查代码块和一个类型安全接口:
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();
}
}
用法非常简单:
panel = PanelChooser.choosePanel(chooser);
答案 3 :(得分:1)
也许你可以这样做:
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;
}
我不用Java编程,但如果是C#,那就是我会尝试的。我也不知道这是否适合你的包。
祝你好运!答案 4 :(得分:0)
我看不到足够的现有代码和设计。所以可能,首先我会尝试将面板实例化的代码移动到创建Changer实例的相同位置。因为选择Panel与选择Changer的决定相同。
如果动态选择了所选的Changer,您可以创建这些面板,然后相应地显示/隐藏它们。
答案 5 :(得分:0)
您的解决方案无效,因为Java会根据编译时类型选择方法(这可能是Changer
)。如果您每次都需要创建新实例,则可以使用Map<Class<? extends Changer>, Panel>
(或Map<Class<? extends Changer>, Class<? extens Panel>>
。如果您需要使用此解决方案(但尚未知) - 例如OrangeChanger
的子类,此解决方案确实需要额外的工作。
例如,每个Changer子类的单个实例
changerToPanel.get(changer.getClass());
或者如果您需要新实例:
changerToPanelClass.get(changer.getClass()).newInstance();
另一种选择是进行初步预感,让Changer了解Panel。
答案 6 :(得分:0)
查看Factory和Abstract Factory模式。
工厂模式是一种创建模式,因为它用于控制类实例化。工厂模式用于替换类构造函数,抽象对象生成过程,以便可以在运行时确定实例化对象的类型。
抽象工厂模式是一种创建模式,因为它用于控制类实例化。抽象工厂模式用于为客户端提供一组相关或依赖对象。工厂创建的对象系列在运行时根据具体工厂类别的选择确定。
答案 7 :(得分:0)
我要做以下事情:
让一个接口PanelChooser使用一个方法返回一个Panel for Changer。
当Change实现某个类时,让实现ClassBasedPanelChooser返回一个面板,否则返回null。要返回的类和面板将在构造函数中传入。
有另一个实现CascadingPanelChooser,它在构造函数参数中获取PanelChoosers列表,并在调用其方法时要求每个PanelChooser提供一个面板,直到它收到一个非null面板,然后返回该面板。
答案 8 :(得分:0)
请勿使用 instanceof 。Why polymorphism fails 唯一使用 instanceof 的地方是等于方法。
回答你的问题。请遵循此link。 致Cowan和jordao的积分。
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) { /* ... */ }
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; } }