【问题标题】:How to get rid of instanceof check when using inheritance?使用继承时如何摆脱instanceof检查?
【发布时间】:2016-12-30 14:57:50
【问题描述】:

假设我们有一个动物类,其子类为猫、鹰

现在我有一个方法:

public void process(Animal animal) {

   if (animal instanceof Cat) {
     if (!animal.meow()) {
        throw exception("cat does not meow");
     } else {
      animal.feedFish();
     }
   }

   if (animal instanceof eagle) {
      if (!animal.fly()) {
         throw exception("eagle does not fly");
      } else {
        animal.checkMaxFlightAltitude();
      }
   }
}

这里 cat 有两个方法 meowfeedfish 与 eagle 的方法 flycheckmaxflight 完全不同

大多数设计模式都围绕着假设子类有一个共同的方法,如 Shape draw() 由 circle draw 和 square draw 继承

  1. 有没有办法对子类进行验证,例如不检查实例的 cat 和 eagle?

  2. 任何好的设计模式(假设子类不共享基类中的方法?)

【问题讨论】:

  • 双发.
  • ^^ 不要浪费时间查看 Double dispatch,这不是一个答案,甚至不是一个好主意。您的问题在其标题中包含答案:您可以使用继承摆脱 instanceof 检查。您目前使用继承。使用继承,然后所有的动物都可以有一个“process()”方法。
  • @JavaDeveloper 这实际上就是Polymorphism 的意义所在!

标签: java oop inheritance design-patterns


【解决方案1】:

您可以在Animal 中有一个抽象的process 方法并在子类中实现它:

class Animal {
  protected abstract void process();
  public static void process(Animal a) { a.process(); }
}

class Cat {
  void process() {
    if (!meow()) throw exception("cat does not meow");
    else feedFish();
  }
  public boolean meow() { ... }
  public void feedFish() { ... }
}

【讨论】:

  • 你可以提到这被称为 visitor 模式。
  • @GhostCat 我不擅长命名模式——这真的是访客模式吗?
  • 我怀疑是这样的。
  • 嗯,有点。不完全是。唔。完整的访问者模式期望 process() 被称为 accept() ;并接受一些访问者对象参数。让我们这么说吧:拥有一个 process() 方法只能解决您最终想要解决的问题的一半。
  • @NickL 你的第二个和第三个问题是。
【解决方案2】:

您可以通过使用visitor 来使用double dispatch

例子:

public class Animal {
    public abstract void accept(AnimalVisitor v);

    public boolean meow() {return false;}
    public boolean fly() {return false;}
    public void feedFish() {};
    public void checkMaxFlightAltitude() {};

}

public class Cat extends Animal {
    public void accept(AnimalVisitor v) {
        v.visitCat(this);
    }

    public boolean meow() {return true;}
}

public class Eagle extends Animal {
    public void accept(AnimalVisitor v) {
        v.visitEagle(this);
    }
    public boolean fly() {return true;}
}

public interface AnimalVisitor {
    void visitEagle(Eagle eagle);
    void visitCat(Cat cat);
}

public class AnimalVisitorExample implements AnimalVisitor {
    public void visitEagle(Eagle eagle) {
        eagle.checkMaxFlightAltitude();
    }

    public void visitCat(Cat cat) {
        cat.feedFish();
    }
}

Animal animal = new Cat();
animal.accept(new AnimalVisitorExample());

【讨论】:

  • 动物为什么会有飞法?
  • 我只是按照 OP 中的示例进行操作。我假设除了说明问题之外,这些方法中的任何一种都没有理由。
  • @massfords OP 声明 meow()feedFish() 属于 Cat,而 fly()checkMaxFlightAltitude() 属于 Eagle。当您将此方法拉入超类时,它会破坏访问者模式的目的。如果您将feedFish()checkMaxFlightAltitude() 移动到相应的类中,而将accept() 作为Animal 中的唯一方法,我认为它会完美地回答这个问题。
  • 我在 cmets 中错过了这一点。我假设所有方法都在 Animal 上,因为在 Animal 上调用它们时缺少演员表。也许这是 OP 中的疏忽,也可能只是伪代码。
【解决方案3】:

(1) 有没有办法对子类进行验证,例如不检查实例的 cat 和 eagle?

是的,有。您可以定义一个“验证”方法(“动物”类中的抽象)并在特定的子类中实现它。根据验证结果(例如异常/问题列表),您可以让 validate 方法抛出某种“InvalidContentException”或为方法调用提供一个“ErrorHandler”,以告知实例的坏事。

(2) 假设子类不共享基类中的方法:嗯,这有点违反直觉。一方面,您希望能够在“动物”上做某事,但又不想在其上定义该功能?

您可以定义一个具有单独验证方法的 Validator 类(针对每种“Animal”子类)。这将消除 instanceof 检查,但是您将永远无法将此 Validator 类传递给其他“Animals”(例如“Dog”),只能传递“Cat”和“Eagle”(或其子类)。您可能还想考虑在传递“Cat”的子类时要发生什么:是否所有 Cat 的子类都以相同的方式验证,或者是否存在针对不同猫类的子类特定行为(如颜色、大小等) ?

--> 我认为你应该问问自己,你是否希望能够验证一般的动物。如果没有深入了解您的问题域(可能有理由不这样做),我建议对动物使用“验证”方法。您也可以使用访问者模式,但这需要 Animal 具有“accept(AnimalVisitor visitor)”方法,并且要编写的代码略多(可能比您想要的更多)

【讨论】:

    【解决方案4】:

    这时多态就派上用场了。

    abstract class Animal {
        abstract public void process(Animal animal);
    }
    class Cat extends Animal {
        @Override
        public void process(Animal animal) {
            if (!this.meow()) {
                throw exception("cat does not meow");
            } else {
                this.feedFish();
            }
        }
    }
    class Eagle extends Animal {
        @Override
        public void process(Animal animal) {
            if (!this.fly()) {
                throw exception("eagle does not fly");
            } else {
                this.checkMaxFlightAltitude();
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2011-09-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-24
      • 2017-10-10
      • 2022-06-11
      • 1970-01-01
      相关资源
      最近更新 更多