【问题标题】:How to use two near-identical classes that do not share the same parent class?如何使用两个几乎相同但不共享同一个父类的类?
【发布时间】:2018-01-25 10:35:57
【问题描述】:

我有 2 个几乎相同的类,理想情况下它们应该共享同一个父类,但不(因为它们来自不同的库,我无法更改其源代码)。

为了举例说明,我有两个类:

public class Cat {
    public void speak() {
        System.out.println("Meow");
    }

    public CatFood findFood() {
        return new CatFood();
    }

    public void eat(CatFood food) {
        System.out.println("[Cat] Yum yum");
    }
}

public class Dog {
    public void speak() {
        System.out.println("Woof");
    }

    public DogFood findFood() {
        return new DogFood();
    }

    public void eat(DogFood food) {
        System.out.println("[Dog] Yum yum");
    }
}

现在理想情况下我想做这样的事情:

Animal[] animals = {new Cat(), new Dog()};
for (Animal animal : animals) {
    animal.speak();
    animal.eat(animal.findFood());
}

CatDog 不继承自 Animal,我无法更改它们的源代码。我还想避免依赖instanceof 作为拐杖:

for (Object animal : animals) {

    if (animal instanceof Dog) {
        Dog dog = (Dog) animal;
        dog.speak();
        dog.eat(dog.findFood());

    } else if (animal instanceof Cat) {
        Cat cat = (Cat) animal;
        cat.speak();
        cat.eat(cat.findFood());

    } else if (animal instanceof Rabbit) { // etc etc
}

代码只是被重复了无数次,所以如果我对逻辑做一点小改动,我也必须复制和粘贴无数次。

那么我怎样才能以最少的代码重复来使用这些类呢?

【问题讨论】:

  • 实现这一点的另一种方法是:Mycat extends Cat implements Animal ,MyDog extends dog implements Animal ,Animal 接口应该有这三种方法
  • 也许你对适配器模式感兴趣。创建您自己的接口和两个实现它并包装 Cat 和 Dog 的类。

标签: java class oop code-duplication


【解决方案1】:

您可以使用适配器模式,但您需要为每种动物实现一个具体的适配器:

interface Animal {
    void speak();
    void eat();
}

class DogAdapter implements Animal {
    private Dog dog;

    public DogAdapter(Dog dog) {
        this.dog = dog;
    }

    public void speak() {
        dog.speak();
    }

    public void eat() {
        dog.eat(dog.findFood());
    }
}

class CatAdapter implements Animal {
    private Cat cat;

    public CatAdapter(Cat cat) {
        this.cat = cat;
    }

    public void speak() {
        cat.speak();
    }

    public void eat() {
        cat.eat(cat.findFood());
    }
}

并且使用工厂可能会封装具体的创建:

class AnimalFactory {
    public static Animal createAdapter(Dog dog) {
        return new DogAdapter(dog);
    }

    public static Animal createAdapter(Cat cat) {
        return new CatAdapter(cat);
    }
}

然后就可以使用Adapter循环运行了:

Animal[] animals = {AnimalFactory.createAdapter(cat), AnimalFactory.createAdapter(dog)};

for (Animal animal : animals) {
    animal.speak();
    animal.eat();
} 

一个痛点是方法eat(),因为DogFood、CatFood……也没有通用的超类型。

【讨论】:

    【解决方案2】:

    如果您无法修改这些类并且希望将代码保持在最低限度,您可以使用反射来调用这些方法。 比如:

    Method method = obj.getClass().getMethod("speak");
    method.invoke(obj);
    

    但考虑仅将反射作为最后的手段

    【讨论】:

      猜你喜欢
      • 2023-03-23
      • 2021-05-08
      • 1970-01-01
      • 1970-01-01
      • 2013-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多