导航不同对象的复杂树的最佳方法是什么?

时间:2015-09-17 23:42:29

标签: java loops design-patterns

例如:

class Vehicle {
    Collection<Axle> axles;
}

class Axle {
    Collection<Wheel> wheels;
}

class Wheel {
    // I think there are dually rims that take two tires -- just go with it
    Collection<Tire> tires;
}

class Tire {
    int width;
    int diameter;
}

我有一项服务,通过该服务我可以获得我所知道的所有车辆对象的集合。现在说我有一个特定宽度和直径的轮胎,我想找一辆可以拿它的车。简单的方法是有一组四个嵌套循环,如下所示:

for (Vehicle vehicle : vehicles) {
    for (Axle axle : vehicle.getAxles()) {
        for (Wheel wheel : axle.getWheels()) {
            for (Tire tire : wheel.getTires()) {
                if (tire.width == targetWidth
                 && tire.diameter == targetDiameter) {
                    // do something
                    break;
                }
            }
        }
    }
}

这有一个好的设计模式吗?还是要使用更好的数据结构?将一个指数保留在轮胎信息映射到车辆的位置会不会更好?

修改:回答评论中的问题

  

您是否可以控制从服务中收到的数据结构?

  

您是否需要在同一数据中多次搜索不同的轮胎?

  

性能是一个问题吗?

不是特别

  

当你找到轮胎时,你只需要知道哪辆车载有它还是你还需要车轴和车轮?

有时只是车辆,有时只是车轴 - 两种不同的背景

  

您是否需要参考找到的轮胎?

是的,在我需要轴的情况下

EDIT2 : 进一步扩展隐喻,解释上述两种情况:

背景1 - 我想知道车辆,所以我可以派一名工人来收集车辆并将其带回来

背景2 - 我想知道轴和轮胎,因为我正在尝试做这项工作的车辆

4 个答案:

答案 0 :(得分:2)

您可以使用Java 8 streams来展平循环。

vehicles.stream()
    .flatMap(vehicle -> vehicle.getAxles().stream())
    .flatMap(axle -> axle.getWheels().stream())
    .flatMap(wheel -> wheel.getTires().stream())
    .filter(tire -> tire.width == targetWidth
             && tire.diameter == targetDiameter)
    .forEach(tire -> {
        // do something
    });

关于流的好处是,您可以在序列中的任何位置轻松插入其他filterfilterfindAny等等。

答案 1 :(得分:2)

我会改变你的逻辑并将问题移到Vehicle,除非你因为任何其他原因而想要保持你的物体变薄(在这种情况下我会亲自用较厚的物体包裹它们)添加所需的任何行为)

class Vehicle {
    ...

    public Tire acceptsTire(Tire tire) {

    }
} 

从这里开始,有几种可能性,具体取决于这个业务逻辑在您的域中的重要性。

  1. 如果你有几个动作,你可能只是像你在样本中那样进行迭代。或者可能以我建议的方式,将问题级联到正确的组件。只要你能忍受这样做的时间复杂性,那就应该没问题。
  2. 如果您通常这样检查,那么您可以直接参考车辆中的轮胎类型,这可能是您的Tire集合,也可以是TireSpecification 1}}构造Vehicle时的实例如果由于任何原因你需要保持这些分开(你的意图在问题上不是很明确,是汽车上的轮胎还是只是适合的规格?)< / LI>

答案 2 :(得分:1)

如果不改变您的数据结构,您将无法发挥重大作用。你可以用lambdas添加一些语法糖,但它基本上是相同的解决方案。

你可以看到的东西:

  • 您的模型允许Vehicles零轴或百轴。虽然这取决于您的商业模式,但似乎很奇怪。
  • 您的车型允许车辆中有不同的车轴,不同的车轮。真的有必要吗?确保模型的哪些元素应该具有单独的标识(当前每个对象都有它),哪个只是一个值对象。
  • 确保您确实需要这样详细的模型。目前,您有两个类(AxleWheel),它们只包含内部对象的集合。如果它们只是具有getAllInnerTypes()的简单JavaBean对象,那么您应该考虑删除此类。甚至可能是轮胎信息应该几乎直接存储在Vehicle类中。

答案 3 :(得分:0)

只要没有太多的项目和/或性能不是一个大问题,我可能会选择嵌套循环(或来自John的回答)。

由于您有两个搜索上下文,您可以将相应的操作传递给搜索方法 - 类似这样(在这种情况下使用循环):

interface TireAction {
    void doSomething(Vehicle v, Axle a, Tire t);
}

void findTireAndPerform(int targetWidth, int targetDiameter, TireAction action) {
    for (Vehicle vehicle : vehicles) {
        for (Axle axle : vehicle.getAxles()) {
            for (Wheel wheel : axle.getWheels()) {
                for (Tire tire : wheel.getTires()) {
                    if (tire.width == targetWidth && tire.diameter == targetDiameter) {
                        action.doSomething(vehicle, axle, tire);
                        break;
                    }
                }
            }
        }
    }
}

void someMethod() {
    ...

    findTireAndPerform(width, diameter, (v, a, t) -> {
        // send worker to 'v'
    });

    ...

    findTireAndPerform(width, diameter, (v, a, t) -> {
        // work on 'a' and 't'
    });
}