【问题标题】:How to DRY these block of code in Java?如何在 Java 中干燥这些代码块?
【发布时间】:2016-09-03 15:33:19
【问题描述】:

来电者:

switch (type){
            case "creature":
                Creature returnActor2 = getNextCreature();
                boolean isEat2 = actOnNearby(getRightChromosome(Config.HardCode.creature), returnActor2.getLocation());
                if (isEat2) {
                    actOnCreature(returnActor2);
                }
                break;
            case "monster":
                Monster returnActor3 = getNextMonster();
                boolean isEat3 = actOnNearby(getRightChromosome(Config.HardCode.monster), returnActor3.getLocation());
                if (isEat3) {
                    actOnMonster(returnActor3);
                }
                break;
}

它将调用以下2个方法:

    private Monster getNextMonster() {
        ArrayList<Actor> nearbyActors = getActors();
        Monster mine = new Monster();
        for (Actor a : nearbyActors) {
            if (a instanceof Monster) {
                mine = (Monster) a;
            }
        }
        return mine;
    }


private Creature getNextCreature() {
    ArrayList<Actor> nearbyActors = getActors();
    Creature mine = new Creature();
    for (Actor a : nearbyActors) {
        if (a instanceof Creature) {
            mine = (Creature) a;
        }
    }
    return mine;
}

问题
可以看到,getNextXXXXX()方法都差不多,只是返回不同的对象,逻辑是一样的,怎么DRY呢? actOnXXXX() 似乎也属于 DRY 类别,但都差不多,对不同的对象使用相同的逻辑。如何解决?

【问题讨论】:

  • 似乎是使用 Java 泛型的好情况
  • CreatureMonster 的哪种行为同时涉及 actOnCreatureactOnMonster ?你能把这些方法的代码和getRightChromosome的代码贴出来吗?
  • @Spotted 不一样,各有各的逻辑:P
  • 在我看来,代码是非常程序化的,如果使用适当的类进行建模,问题可能会自行解决?一个迹象是几乎没有提到类,只有方法。例如,我可以想象(不知道具体细节)Actor.actOnNearby() 方法,它可以解决我假设的一些“强制转换”。

标签: java class oop object types


【解决方案1】:

让它接受一个类类型:

private <T> T getNext(Class<T> type) {
    for (Actor a : getActors()) {
        if (type.isAssignableFrom(a.getClass())) {
            return (T) a;
        }
    }
    return null; //or type.newInstance(); if you want a guaranteed object, but this restricts your constructor.
}

或者使用 Java 8:

private <T> T getNext(Class<T> type) {
    return (T) getActors().stream()
                .filter(a -> type.isAssignableFrom(a.getClass()))
                .findFirst().orElse(null);
}

但是用法是一样的:

Monster next = getNext(Monster.class);

分解问题,你知道两类事情:

你需要什么:

  • t 类型的下一个对象。
  • 一种判断对象是否为t的方法 输入

你有什么:

  • 你想要的t类型
  • 对象的集合,其中一个可能是t 类型
  • 如果没有,则通过无参数构造函数(或 null)创建一个新对象

此外,所有这些方法之间的唯一区别是一件事:它是哪种类型。所以我们从字面上“把它变成一个变量”,因此它变成了一个方法参数。

分解它,我们只需要以一种完成此的方式组织代码:

method: //receives a "type" as a parameter
    iterate the list of possible `t`s //our list of objects
        if some_t == type //our comparison, previously `a instanceof Type`
            return some_t //our result is found
    return null //or a new object, but essentially our "default"

这里唯一的主要区别是:

  1. some_t instanceof Type 替换为type.isAssignableFrom(some_t.getClass())

出现在这里的原因是您在使用 Class&lt;T&gt; 时如何确定这一点

  1. 我们的默认值可以是 null 也可以是新对象

通过反射动态地创建对象会限制您的选择,并且需要处理异常。返回null 或空的Optional&lt;T&gt; 将有助于表明您没有结果,调用者可以采取相应的行动。您也可以只传递默认对象本身,然后返回instanceof 检查。

问自己“我需要什么,我可以提供/拥有什么”的相同假设,将帮助您将问题分解为更小的步骤,并解决更大的难题。

【讨论】:

  • 感谢您的解决,Java 8 版本看起来非常通俗易懂和优雅。谢谢!
【解决方案2】:

我认为,您的代码和逻辑存在混淆。 例如,如果您需要迭代列表,则不需要创建新对象。即在下面的代码sn-p中,“new Monster()”就不用写了

Monster mine = null; // new Monster();
for (Actor a : nearbyActors) {
    if (a instanceof Monster) {
        mine = (Monster) a;
    }
}

无论如何,答案是“Java 中的类型推断”。 https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html

你的问题的答案是

package __TypeInference;

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

public class Main {

public static void main(String[] args) {
    new Main().doLogic();
}

private void doLogic() {
    List<Actor> nearbyActors = getActors();
    for (Actor actor : nearbyActors) {
        // do with the next actor
        System.out.println(actor.toString());
    }
}

private List<Actor> getActors() {
    List<Actor> actors = new ArrayList<Actor>();
    actors.add(new Monster());
    actors.add(new Creature());
    actors.add(new Monster());
    actors.add(new Creature());
    return actors;
}

class Monster extends Actor {
    @Override
    public String toString() {
        return "Monster";
    }
}

class Creature extends Actor {
    @Override
    public String toString() {
        return "Creatue";
    }
}

class Actor {
}
}

【讨论】:

    【解决方案3】:

    我认为你想要的是将getNextMonstergetNextCreature 组合起来,因为它们有重复的代码。

    这里最好的做法是编写一个通用方法来执行此操作:

    private <T extends Actor> T getNextActor(T newActor) {
        ArrayList<Actor> nearbyActors = getActors();
        T mine = newActor;
        for (Actor a : nearbyActors) {
            if (a instanceof T) {
                mine = (T) a;
            }
        }
        return mine;
    }
    

    你可以这样称呼它:

    // This is equivalent to calling getNextCreature()
    getNextActor(new Creature());
    
    // This is equivalent to calling getNextMonster()
    getNextActor(new Monster());
    

    让我解释一下代码。

    新方法返回Actor 类型。你通过传递参数告诉它你想要什么样的演员。该参数是必要的,因为您不能像这样初始化泛型类型参数:

    new T();
    

    因为无参数构造函数可能不可用。这就是调用者的工作。

    我真的不知道我在说什么......

    这种方法有以下优点:

    • 减少重复代码
    • 它很灵活 - 当您想要添加另一个名为 getNextXXX 的方法(其中 XXXActor 的子类)时,您不需要这样做。只需致电getNextActor(new XXX())
    • 它增加了可维护性 - 如果您想更改 getNextXXX 的实现,您可以只更改一种方法而不是 2。

    【讨论】:

    • 感谢解答,非常有可读性,甚至用我不好的代码风格也好抓,哈哈,甜心,太感谢了!实际上,起初,我尝试用唯一的字母“T”来实现该方法,似乎行不通:P
    猜你喜欢
    • 2013-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-18
    相关资源
    最近更新 更多