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

用Java重构代码,替代大型if语句,java,refactoring,Java,Refactoring,我正在重构一个项目中的一些代码,我遇到了一个大的if/elseif语句,其格式如下: if (changer instanceof AppleChanger) { panel = new ApplePanel(); } else if (changer instanceof OrangeChanger) { panel = new OrangePanel(); } 现在我的第一个冲动是使用多态性重构它,使它看起来像 panel = changer.getChangerPanel(

我正在重构一个项目中的一些代码,我遇到了一个大的if/elseif语句,其格式如下:

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;
}
这是一个好的解决方案还是有更好的方法解决这个问题?

也许您可以:

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#,我会尝试这样做。我也不知道这是否适用于您的软件包


祝你好运

我没有看到足够的现有代码和设计。因此,首先,我可能会尝试将带有面板实例化的代码移动到创建Changer实例的同一位置。因为选择一个面板和选择一个转换器是相同的决定


如果动态选择了一个选定的转换器,您可以创建这些面板,然后相应地显示/隐藏它们。

我认为您的第一个脉冲不起作用是好的:)否则您会将转换器代码(应该是逻辑代码)与UI代码(面板)耦合,这是错误的

现在,我可以为您提供以下解决方案:

使用方法Panel createPanel创建接口PanelCreator,如下所示:

interface PanelCreator {
   Panel createPanel();
}
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 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());
我认为它将更加优雅,因为您可以在给定更改器的情况下轻松更改创建者的实现


希望这有帮助

这里的基本“问题”是您有并行类层次结构。如果不进行一些相当繁重的重构,您将无法替换if语句。以下是一些建议


您所能做的最好的,也可能是一个非常好的解决方案,就是将if语句移动到一个“factory”类中,并确保它不会复制到其他任何地方。

您的解决方案将不起作用,因为Java根据compiletime类型选择方法(这里可能是
Changer
)。您可以使用
Map查看和模式

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

抽象工厂模式是一种创造性模式,用于控制类实例化。抽象工厂模式用于向客户机提供一组相关或从属对象。工厂创建的对象族在运行时根据具体工厂类的选择确定。

我将执行以下操作:

有一个接口PanelChooser,它使用一个方法为转换器返回一个面板

让实现ClassBasedPanelChooser在更改实现某个类时返回面板,否则返回null。将要返回的类和面板传递到构造函数中


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

不要使用instanceof。 唯一可以使用instanceof的地方是equals方法内部

回答你的问题。跟着这个。 归功于和

  • 使用反射。
  • 责任链

如果代码中存在多个依赖于转换器实例类型的If/else构造,则可以使用如下访问者模式:

interface PanelCreator {
   Panel createPanel();
}
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;
  }
}
用法非常简单:

panel = PanelChooser.choosePanel(chooser);

我怀疑第二个方法不起作用,因为您必须显式地将转换器转换为java的子类,以标识要调用的方法。也许你可以使用注册表类/映射(这样就可以将更改器映射到面板并查找传递的实例)。为什么你的类名以小写开头?@U Mad Thank,这是我的错别字,fixedOk,但这需要你的类的命名约定;每个
xyzChanger
类必须有一个
xyzPanel
。而且它并不是真正的类型安全,因为有一个转换到
面板
那里。IMHO,最好避免对类名进行这样的操作,因为它不是唯一的选项。它更难维护,例如,类名更改不会导致任何编译错误。是的,这将是一种命名约定。公约应该加速发展。但是,
getPanel
应该可以覆盖,以防您想要更改某些
更改器的约定。如果你想知道的话,这将是抽象工厂设计模式。这很有趣,我将搜索是否可以用Java实现它,谢谢。那已经是Java了,除非我在某个地方犯了错误:-)一个很好的快速解决方案,但是,如果有人添加了一个子类,例如OrangeChanger,那么它将不起作用。您认为在这种情况下,注册表应该通过附加的“put”调用进行更新吗?您可以这样做,但您违反了Liskov替换原则。就实现相同结果所花费的代码行数而言,与if语句相比没有任何优势。理论上你是对的,但这里的问题是创建这样的子类型是否合理。您也说得对,当前的实现没有优势,但主要思想是将面板的条件创建逻辑与转换器解耦。在实际情况下,最好的设计不是简单的映射,而是更高级的配置(可能是基于xml的spring或其他配置)。在中国科学院
  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;
   }
}
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);