java - 合理 `instanceof` ?将其与接口(interface)一起使用,但不与实现类型一起使用

标签 java design-patterns instanceof

当代码中包含 Java instanceof 运算符时,许多人会扬起眉毛并说这是禁忌。例如,在这个 other SO Q&A ,答案说:

Note that if you have to use that operator very often it is generally a hint that your design has some flaws. So in a well designed application you should have to use that operator as little as possible (of course there are exceptions to that general rule).

但是,它没有进一步详细说明什么时候可以使用 instanceof ,什么时候可以不使用。

我对此进行了一些思考,并阐明了以下指导方针。我认为这可能在互联网上的某个地方讨论过,但我找不到。因此这个问题并征求您的评论:

Using instanceof on an interface is okay; using instanceof on an implementation is not okay

这是“好的”案例的示例。

示例:动物目录,其中一些(但不是全部)可以飞

Animal.java

public interface Animal {
    String getName();
    String makeNoise();
}

CanFly.java

public interface CanFly {
    float getMaxInAirDistanceKm();
}

Cat.java

public class Cat implements Animal {
    @Override
    public String getName() {
        return "Cat";
    }

    @Override
    public String makeNoise() {
        return "meow";
    }
}

BaldEgale.java

public class BaldEagle implements Animal, CanFly {
    @Override
    public String getName() {
        return "BaldEagle";
    }

    @Override
    public String makeNoise() {
        return "whistle";
    }

    @Override
    public float getMaxInAirDistanceKm() {
        return 50;
    }
}

目录.java

import java.util.ArrayList;
import java.util.List;

public class Catalog {
    private List<Animal> animals = new ArrayList<>();

    public void putAnimal(Animal animal) {
        animals.add(animal);
    }

    public void showList() {
        animals.forEach(animal -> {
            StringBuilder sb = new StringBuilder();
            sb.append(animal.getName() + ": ");
            sb.append(animal.makeNoise() + " ");

            // this block exemplifies some processing that is 
            //   specific to CanFly animals
            if (animal instanceof CanFly) {
                sb.append(String.format(" (can stay in air for %s km)",
                        ((CanFly) animal).getMaxInAirDistanceKm()));
            }
            System.out.println(sb.toString());
        });
    }

    public static void main(String[] args){

        Catalog catalog = new Catalog();
        Cat cat = new Cat();
        BaldEagle baldEagle = new BaldEagle();
        catalog.putAnimal(cat);
        catalog.putAnimal(baldEagle);

        catalog.showList();
    }
}

测试输出

Cat: meow 
BaldEagle: whistle  (can stay in air for 50.0 km)

更新于 2019-10-09 添加“不好”案例的示例:

我们可以删除 CanFly 接口(interface),并在 showList() 方法中,将 instanceof 应用于具体实现 BaldEagle ——如下所示:

    public void showList() {
        animals.forEach(animal -> {
            StringBuilder sb = new StringBuilder();
            sb.append(animal.getName() + ": ");
            sb.append(animal.makeNoise() + " ");

            if (animal instanceof BaldEagle) {
                sb.append(String.format(" (can stay in air for %s km)",
                        ((BaldEagle) animal).getMaxInAirDistanceKm()));
            }
            System.out.println(sb.toString());
        });
    }

这种方法不好,因为代码现在依赖于实现,而不是接口(interface)。例如,它可以防止替换代表 Bald Eagle 的另一个实现(例如 BaldEagleImpl)

最佳答案

我认为人们认为总有一个“更干净”的解决方案来产生您想要的行为。

在您的示例中,我想说,使用 Visitor 设计模式在不使用 instanceOf 的情况下执行完全相同的操作:

public interface Animal {
    String getName();
    String makeNoise();
    void accept(AnimalVisitor v);
}

public interface AnimalVisitor() {
    void visit(Cat a);
    void visit(BaldEagle a);
}

public interface CanFly {
    float getMaxInAirDistanceKm();
}

public class Cat implements Animal {
    void accept(Visitor v) {
        v.visit(this);
    }
}

public class BaldEagle implements Animal, CanFly {
    void accept(Visitor v) {
        v.visit(this);
    }
}

public class DisplayVisitor implements AnimalVisitor  {
    void visit(Cat a) {
       //build & display your string
    }

    void visit(BaldEagle a) {
       //build & display your string
    }
}

public class Catalog {
    private List<Animal> animals = new ArrayList<>();

    public void putAnimal(Animal animal) {
        animals.add(animal);
    }

    public void showList() {
        DisplayVisitor display = new DisplayVisitor();
        animals.forEach(a->a.accept(display));
    }
}

虽然我没有完全回答你的问题,但它表明在大多数情况下,只需以 OOP 方式思考并使用已知模式,无需使用 instanceOf 即可完成相同的行为。

关于java - 合理 `instanceof` ?将其与接口(interface)一起使用,但不与实现类型一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58300819/

相关文章:

java - 使用oracle函数时如何在mybatis中将参数作为CLOB传递?

java - NoClassDefFoundError 没有意义

java - 通过反射检查 Class<?> 是否是 other 的实例

c# - 日志记录结构 - 当必须记录每个方法时如何构建?

java if 语句在 if(item instanceof nomclass) 中不起作用

java - 替换 Java Bean 的 instanceof

java - 从列表中删除数据后如何更新 ListView ?

java - 不允许使用 try catch block 进行基本输入计算

objective-c - 将枚举转换为类层次结构

java - 如何在组件的设计阶段防止内存泄漏?