为什么不在OOP设计中使用instanceof运算符?

use*_*741 6 oop instanceof

已经反复说过,除了equals()方法之外,不应该使用instanceof运算符,否则它是一个糟糕的OOP设计.

有人写道,这是一个繁重的操作,但似乎,至少java,处理得相当好(甚至比Object.toString()比较更有效).

有人可以解释,或指导我一些文章,解释为什么这是一个糟糕的设计?

考虑一下:

Class Man{
  doThingsWithAnimals(List<Animal> animals){
    for(Animal animal : animals){
      if(animal instanceOf Fish){
        eatIt(animal);
      }
      else if(animal instanceof Dog){
        playWithIt(animal);
      }
    }
  }
  ...
}
Run Code Online (Sandbox Code Playgroud)

决定如何处理动物,取决于男人.男人的欲望也可能偶尔改变,决定吃狗,玩鱼,而动物不会改变.

如果您认为instanceof运算符不是正确的OOP设计,请告诉您如何在没有instanceof的情况下执行此操作,为什么?

谢谢.

Mik*_*378 11

instanceof简单地打破了开/关原则.和/或Liskov替代原则

如果由于instanceof使用我们不够抽象,每次新的子类进入时,收集应用程序逻辑的主代码可能会更新.这显然不是我们想要的,因为它可能会破坏现有代码并降低其可重用性.

因此,优先使用多态性应优先于条件的基本使用.


Rad*_*def 8

有一篇很好的博客帖子叫做" 当多态性失败"这个场景.基本上,你是对的,应该Man决定如何处理每种类型Animal.否则,代码就会变得支离破碎,最终会违反单一责任德米特定律等原则.

拥有例如以下代码是没有意义的:

abstract class Animal {
    abstract void interactWith(Man man);
}
class Fish extends Animal {
    @Override
    void interactWith(Man man) {
        man.eat(this);
    }
}
class Dog extends Animal {
    @Override
    void interactWith(Man man) {
        man.playWith(this);
    }
}
Run Code Online (Sandbox Code Playgroud)

在那个例子中,我们把Man逻辑放在了Man课堂之外.

问题instanceof在于,如果你有大量的Animals,你最终会得到很长的s if-else-if.很难维护并且容易出现错误,例如Animal添加了新类型,但是您忘记将其添加到if-else-if链中.(访问者模式部分是后一个问题的解决方案,因为当您向访问者类添加新类型时,所有实现都会停止编译,并且您不得不全部更新它们.)

但是,我们仍然可以使用多态来使代码更简单并避免instanceof.

例如,如果我们有一个喂养程序,例如:

if (animal instanceof Cat) {
    animal.eat(catFood);
} else if (animal instanceof Dog) {
    animal.eat(dogFood);
} else if (...) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

我们可以消除if-else-if由有方法,如Animal.eat(Food)Animal.getPreferredFood():

animal.eat(animal.getPreferredFood());
Run Code Online (Sandbox Code Playgroud)

使用诸如Animal.isFood()和之类的方法Animal.isPet(),问题中的示例可以不用instanceof如下方式编写:

if (animal.isFood()) {
    eatIt(animal);
} else if (animal.isPet()) {
    playWithIt(animal);
}
Run Code Online (Sandbox Code Playgroud)


pyo*_*yon 1

instanceof是一个类型系统逃生舱口。它可以用来做一些非常邪恶的事情,例如使泛型不再真正泛型,或者使用从未出现在这些类的可见接口中的临时虚拟方法来扩展类层次结构。这两件事都不利于长期可维护性。

通常,如果您发现自己想要使用instanceof,则意味着您的设计有问题。破坏类型系统应该始终是最后的手段,而不是掉以轻心的事情。

我认为您的特定示例不值得使用instanceof. 面向对象的方法是使用访问者模式

abstract class Animal {
  def accept(v: AnimalVisitor)
}

trait Edible extends Animal {
  def taste : String
  def accept(v: AnimalVisitor) = v.visit(this)
}

trait Pet extends Animal {
  def growl : String
  def accept(v: AnimalVisitor) = v.visit(this)
}

abstract class AnimalVisitor {
  def visit(e: Edible)
  def visit(p: Pet)
}

class EatOrPlayVisitor {
  def visit(e: Edible) = println("it tastes " + e.taste)
  def visit(p: Pet) = println("it says: " + p.growl)
}

class Chicken extends Animal with Edible {
  def taste = "plain"
}

class Lobster extends Animal with Edible {
  def taste = "exotic"
}

class Cat extends Animal with Pet {
  def growl = "meow"
}

class Dog extends Animal with Pet {
  def growl = "woof"
}

object Main extends App {
  val v = new EatOrPlayVisitor()
  val as = List(new Chicken(), new Lobster(), new Cat(), new Dog())
  for (a <- as) a.accept(v)
}
Run Code Online (Sandbox Code Playgroud)

注意:我知道 Scala 有案例类,但我想提供一个通用的面向对象的解决方案。