有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java重构严格的代码以消除检查instanceOf的ifelse语句

大家好,

我有一个关于重构一段代码的问题。这些课程的结构如下:

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模式,但现在我认为Decorator模式不能解决我的问题。我想问一下,你能不能建议我一种重构上述代码的方法,以消除if-else语句的使用?(请注意,我无法将AdditionalStuff的函数添加到内部,因为它们的使用方式不同)


共 (6) 个答案

  1. # 1 楼答案

    经典的“文本书方法”是访问者模式

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

    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需要recieve方法的实现:

    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 ...)方法不同的是:visitor方法将保证您不会错过特殊的实例检查。如果您忘记添加用于处理A4类的实现,编译器将告诉您。 编辑:修复实现类的代码

  2. # 2 楼答案

    使用接口

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

    您可以定义:

    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
    

    可以使用参数(对象…参数)控制其他行为

  3. # 3 楼答案

    您可以尝试以下方法

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

    有了它,只需更改列表以容纳各种接口实现,例如:

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

    然后,只需在一行中遍历所有内容,然后执行以下操作:

    list.forEach(SomeInterface::function);
    

    通过这种方式,您将避免复杂的继承(这里不需要),以及内省和其他一切

  4. # 4 楼答案

    对此有多种方法。但是扩展现有实现的最简单答案如下所示

    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()
        }
    
    }
    
  5. # 5 楼答案

    我可以想出两个好办法。两者都是“工厂”设计模式的变体

    方法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是将测试实例抽象为工厂方法:

    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");
        }
    }
    

    您可以用switch替换if instanceof测试,并打开instance的类名。但对于少数子类来说,这样做几乎没有什么好处。(海事组织)

  6. # 6 楼答案

    在不理解代码的实际用途的情况下提出合理的建议有点困难,但我会研究工厂模式,即为每个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”委托调用,从而得到类似registry.createFor(instance)的结果,但你应该明白这一点

    添加另一个AdditionalStuff将需要以下内容:

    • 创建附加类(您总是这样做)
    • 为该类创建工厂(除非您可以提供一些通用工厂)
    • 在您的注册表中注册新工厂(这可以通过一些查找机制来完成,例如通过CDI,但我不会在这里深入讨论)