用Java重构代码,替换大型if语句

时间:2012-01-10 08:34:43

标签: java refactoring

我正在重构我正在处理的项目中的一些代码,并且遇到了一个大的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;
}

这是一个很好的解决方案还是有更好的解决方法?

9 个答案:

答案 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)

查看FactoryAbstract Factory模式。

工厂模式是一种创建模式,因为它用于控制类实例化。工厂模式用于替换类构造函数,抽象对象生成过程,以便可以在运行时确定实例化对象的类型。

抽象工厂模式是一种创建模式,因为它用于控制类实例化。抽象工厂模式用于为客户端提供一组相关或依赖对象。工厂创建的对象系列在运行时根据具体工厂类别的选择确定。

答案 7 :(得分:0)

我要做以下事情:

让一个接口PanelChooser使用一个方法返回一个Panel for Changer。

当Change实现某个类时,让实现ClassBasedPanelChooser返回一个面板,否则返回null。要返回的类和面板将在构造函数中传入。

有另一个实现CascadingPanelChooser,它在构造函数参数中获取PanelChoosers列表,并在调用其方法时要求每个PanelChooser提供一个面板,直到它收到一个非null面板,然后返回该面板。

答案 8 :(得分:0)

请勿使用 instanceof Why polymorphism fails 唯一使用 instanceof 的地方是等于方法。

回答你的问题。请遵循此link。 致Cowanjordao的积分。

  • 使用反射。
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;
   }
}