重构刚性代码以消除检查instanceOf的if-else语句

时间:2018-08-02 14:03:50

标签: java inheritance design-patterns refactoring

大家好,

我有一个关于重构一段代码的问题。这些类的结构为:

abstract class A
class A1 extends class A
class A2 extends class A
class A3 extends class A

abstract class AdditionalStuff {
    abstract void function(); 
}
class AdditionalStuffForA1 extends AdditionalStuff
class AdditionalStuffForA2 extends AdditionalStuff
class AdditionalStuffForA3 extends AdditionalStuff

class Implementation {
    List<A> aList; 
    .... //add A1, A2, A3 to aList

    AdditionalStuff aS;

    for (A instance: aList) {
        if(instance instanceOf A1)
            aS = new AdditionalStuffForA1();
        else if(instance instanceOf A2)
            aS = new AdditionalStuffForA2();
        else
            aS = new AdditionalStuffForA3();

        aS.function()
    }

}

我认为上面的代码很严格,因为每次添加新的类An(例如A4和AdditionalStuffForA4)时,也必须修改if else语句。

我曾考虑过使用Decorator Pattern,但现在我认为Decorator Pattern无法解决我的问题。我想问一下,您能否建议我一种重构上述代码的方法,以消除使用if-else语句? (请注意,我不能将AdditionalStuff的函数添加到A内,因为它们的用法不同)

7 个答案:

答案 0 :(得分:3)

对此有多种方法。但是扩展现有实施的最简单答案就是这样

abstract class A { 
    public abstract AdditionalStuff getAdditionalStuff();
}

class A1 extends class A { 
    @Override
    public AdditionalStuff getAdditionalStuff() {
       return new AdditionalStuffA1();
    }
}

class A2 extends class A { 
    @Override
    public AdditionalStuff getAdditionalStuff() {
       return new AdditionalStuffA2();
    }
}

class A3 extends class A { 
    @Override
    public AdditionalStuff getAdditionalStuff() {
       return new AdditionalStuffA3();
    }
}

abstract class AdditionalStuff {
    abstract void function(); 
}
class AdditionalStuffForA1 extends AdditionalStuff
class AdditionalStuffForA2 extends AdditionalStuff
class AdditionalStuffForA3 extends AdditionalStuff

class Implementation {
    List<A> aList; 
.... //add A1, A2, A3 to aList

    AdditionalStuff aS;

    for (A instance: aList) {
        aS = instance.getAdditionalStuff();
        aS.function()
    }

}

答案 1 :(得分:2)

经典的“课本方法”就是“访客模式”。

首先,您需要定义一个访问者界面:

interface AVisitor<T> {
      T visitA1(A1 a1);
      T visitA2(A2 a2);
      T visitA3(A3 a3);
}

然后,您需要通过一种抽象方法扩展A类以接收访问者:

abstract class A {
      ...

      public <T> abstract T recieve(AVisitor<T> visitor);
}

您的类A1A2A3需要receve方法的实现:

class A1 extends A {
      ...
      public <T> T recieve(AVisitor<T> visitor) {
          return visitor.visitA1(this);
      }
}

class A2 extends A {
      ...
      public <T> T recieve(AVisitor<T> visitor) {
          return vistor.visitA2(this);
      }
}

class A3 extends A {
      ...
      public <T> T recieve(AVisitor<T> visitor) {
          return visitor.visitA3(this);
      }
}

最后,您必须在实现类中定义不同的visit方法:

class Implementation implements AVisitor<AdditionalStuff> {

      void ... () {
          List<A> aList;
          ....

          for (A instance : aList) {
              AdditionalStuff aS = instance.recieve(this);
              aS.function();
          }
      }

      public AdditionalStuff visitA1(A1 a1) {
          return new AdditionalStuffForA1();
      }

      public AdditionalStuff visitA2(A2 a2) {
          return new AdditionalStuffForA2();
      }

      public AdditionalStuff visitA3(A3 a3) {
          return new AdditionalStuffForA3();
      }
}

此方法的优点是您的A,A1,A2和A3类不需要任何关于这些AdditionalStuff类的知识。缺点是,当您需要第四类A4时,必须在接口中添加visitA4方法,并且在实现此AVisitor接口的每个类中都需要添加方法。 (编辑:但是与您的if (... instanceof ...) else if (... instanceof ...)方法不同:访问者方法将确保您不会错过特殊实例检查。如果您忘记添加用于处理A4类的实现,则编译器会告诉您。 编辑:修复实现类的代码

答案 2 :(得分:1)

我可以想到两种好的方法。两者都是“工厂”设计模式的变体。

方法1是使A类负责创建AdditionalStuff子类型的实例。

abstract class A {
    abstract AdditionalStuff makeAdditionalStuff();
    ...
}

class A1 extends class A {
    AdditionalStuff makeAdditionalStuff() {
        return new AdditionalStuffForA1();
    ...
}

class Implementation {
    List<A> aList; 
    .... //add A1, A2, A3 to aList

    for (A instance: aList) {
        instance.getAdditionalStuff().function()
    }
}

方法2是将instanceOf测试抽象为一种工厂方法:

AdditionalStuff getAdditionalStuff(A instance) {
    if (instance instanceOf A1) {
        return new AdditionalStuffForA1();
    } else if(instance instanceOf A2) {
        return new AdditionalStuffForA2();
    } else {
        return new AdditionalStuffForA3();
    }
}

或更好:

AdditionalStuff getAdditionalStuff(A instance) {
    if (instance instanceOf A1) {
        return new AdditionalStuffForA1();
    } else if (instance instanceOf A2) {
        return new AdditionalStuffForA2();
    } else if (instance instanceOf A3) {
        return new AdditionalStuffForA3();
    } else {
        throw new RuntimeException("not implemented");
    }
}

您可以将if instanceof测试替换为switch,并打开instance的类名。但是对于少数子类而言,这样做几乎无济于事。 (IMO)

答案 3 :(得分:1)

在不了解您的代码实际上是要做什么的情况下提出明智的建议有点困难,但是我会研究工厂模式,即为每个AdditionalStuff子类提供一个工厂,在一些地图中注册它,然后通过实例的类查找它:

interface AdditionalStuffFactory {
  AdditionalStuff create();
}

class AdditionalStuffA1Factory implements AdditionalStuffFactory { 
  public AdditionalStuff create() {
    return new AdditionalStuffForA1();
  }
}

然后您在某个地方有一个Map<Class<? extends A>, AdditionalStuffFactory>,那里将充满工厂实例,例如registry.put(A1.class, new AdditionalStuffA1Factory() );

您的循环将如下所示:

 for (A instance: aList) {
   AdditionalStuff aS = registry.get(instance.getClass()).create();
   aS.function();
 }

当然还有进一步的优化,例如让“注册表”委派电话,从而得到类似registry.createFor(instance)的信息,但您应该明白这一点。

再添加另一个AdditionalStuff,则需要满足以下条件:

  • 创建其他课程(您总是这样做)
  • 为该类创建工厂(除非您可以提供一些通用工厂)
  • 在您的注册表中注册新工厂(可以通过某种查找机制(例如,通过CDI来完成),但在这里我不做进一步介绍)

答案 4 :(得分:1)

您可以尝试以下方法。

首先,如果不需要,请避免使用继承。例如,您的AdditionalStuff仅使用方法function定义了一个合同,该合同需要由您拥有的各种类来实现。可以将其更改为interface类,而不是abstract类。

有了这个,只需更改列表即可容纳各种接口实现,例如:

List<SomeInterface> list = new ArrayList<>;
Collections.addAll(list, new ClassA(), new ClassB());

然后只需一行就可以遍历所有对象,然后执行以下操作:

list.forEach(SomeInterface::function);

通过这种方式,您将避免复杂的继承(此处未进行继承)以及自省和其他所有内容。

答案 5 :(得分:1)

使用界面!

在Java 8或更高版本中,接口可以具有默认的方法实现。

您可以定义:

public interface AdditionalStuff {

    void function(Object... params);
}

public interface AdditionalStuffForA1 extends AdditionalStuff {

    default void function(Object... params) {
        //do something with params here
        System.out.println("implementation for A1");
    }
}

public interface AdditionalStuffForA2 extends AdditionalStuff {

    default void function(Object... params) {
        //do something with params here
        System.out.println("implementation for A2");
    }
}

并使用适当的接口创建类:

public abstract class A implements AdditionalStuff {

}

public class A1 extends A implements AdditionalStuffForA1 {

}

public class A2 extends A implements AdditionalStuffForA2 {

}

执行此操作:

List<A> aList = new ArrayList<>();

aList.add(new A1());
aList.add(new A2());
aList.add(new A2());

for (A a : aList) {
     a.function(23, 45, "string...");
}

将打印:

implementation for A1
implementation for A2
implementation for A2

可以通过参数(对象...参数)控制其他行为

答案 6 :(得分:0)

抽象或接口实现上自己的方法可以自我验证,从而避免了很多(如果只)在此实现的每个实现中使用一个if。将其添加到@Lup的architecture上。