简体   繁体   English

检查 List 中的元素是否有效且顺序正确

[英]Check if elements in List are valid and in the right order

I am making a replica of a Subway restaurant where you would receive an order in a certain sequence and check if the sequence is valid and if the ingredients are in the menu.我正在制作 Subway 餐厅的复制品,您将在其中按特定顺序收到订单,并检查该顺序是否有效以及菜单中是否有食材。

The right order is: 1 bread, 0 to 1 meat, 1 cheese, 1 to 3 extras, 1 to 3 sauces.正确的顺序是:1 块面包、0 到 1 块肉、1 块奶酪、1 到 3 份额外食物、1 到 3 种酱汁。

Meaning that an order can have a minimum of 4 ingredients (bread, cheese, 1 extra, 1 sauce) and a maximum of 9 ingredients (bread, meat, cheese, 3 extras, 3 sauces).这意味着一个订单可以包含至少 4 种成分(面包、奶酪、1 种额外成分、1 种酱汁)和最多 9 种成分(面包、肉类、奶酪、3 种额外成分、3 种酱汁)。

My question is if there is a more optimized/smarter method to go about validating each and every ingredient than mine?我的问题是,是否有比我的更优化/更智能的方法来验证每种成分?

Code:代码:

// Example order
HashMap<String, HashSet<String>> menu = new HashMap<>();

public static void main(String[] args) {
    // Example order
    List<String> ingredients = Arrays.asList("Wheat", "Veal",
        "Yellow", "Cucumbers", "Onions");
    if (!isValid(ingredients)) {
        // throw exc;
}
    
    
    
boolean isValid(List<String> ingredients) {
    if (ingredients.size() < 4 || ingredients.size() > 9) {
        return false;
    }
    int i = 0;
    // Bread
    if (!Restaurant.menu.get("Bread")
            .contains(ingredients.get(i++))) {
        System.out.println("Bread");
        return false;
    }
    
    // Meat
    if (!(Restaurant.menu.get("Meat")
            .contains(ingredients.get(i)))
            && !Restaurant.menu.get("Cheese")
                    .contains(ingredients.get(i))) {
        System.out.println("Meat");
        return false;
    }
    
    if (Restaurant.menu.get("Meat")
            .contains(ingredients.get(i))) { // Meat case
        if ((!Restaurant.menu.get("Cheese")
                .contains(ingredients.get(++i)))) {
            System.out.println("Cheese");
            return false;
        }
    }
    
    for (int j = ++i; j < ingredients.size(); j++) {
        if ((!Restaurant.menu.get("Extras")
                .contains(ingredients.get(j)))) { // Extras
            if (j == i) {
                return false;
            } else {
                if ((!Restaurant.menu.get("Sauces")
                        .contains(ingredients.get(j)))) { // Sauces
                    return false;
                }
            }
        }
    }
    
    return true;
}

Note 1: I know about the rule "If it works, don't touch it" but I feel like this code is getting in the territory of spaghetti code with a bunch of ifs essentially checking similar things with lots of cases and just wanted a second opinion if there is a more optimized way I can't think of right now.注意 1:我知道“如果它有效,请不要碰它”的规则,但我觉得这段代码正在进入意大利面条代码的领域,其中有一堆 ifs 基本上检查了很多情况下类似的事情,只是想要一个第二种意见,如果我现在想不出更优化的方式。

Note 2: I chose HashSet over ArrayList for the menu because it is faster to search.注意 2:我为菜单选择了 HashSet 而不是 ArrayList,因为它的搜索速度更快。

The problem I see with your proposed solution is that it is trying to solve all problems at once, instead of solving them separately.我在您提出的解决方案中看到的问题是它试图一次解决所有问题,而不是单独解决它们。 That makes your code hard to read and understand, in my opinion.在我看来,这使您的代码难以阅读和理解。 While it may work, the more business rules you add, the harder this will get.虽然它可能有效,但您添加的业务规则越多,这就越难。

So what can you do about it?所以你对此能做些什么? Separate the concerns.分离关注点。

The first concern is cassifying an ingredient: is it bread, cheese, meat, extra, sauce?第一个问题是对一种成分进行分类:是面包、奶酪、肉、额外的还是酱汁? You could eg create a class Menu with a method getCategory() (instead of just using a HashSet for menu) that returns the category, and the return value could be an Enum.例如,您可以使用返回类别的方法getCategory() (而不仅仅是对Menu使用HashSet )创建一个菜单类,并且返回值可以是一个枚举。

The seond concern is order.第二个问题是秩序。 You could check the order of the list using a custom Comparator .您可以使用自定义Comparator检查列表的顺序。 See this question for details.有关详细信息,请参阅此问题

The third concern is number of ingredients of a certain category.第三个关注点是某一类别的成分数量。 Given that you can find out the category of an ingredient, you can count how many you have and check if it is the right amount.鉴于您可以找出一种成分的类别,您可以计算您有多少并检查它是否正确。

There are more things to be said about how to achieve any of this, I just wanted to point you in a possible direction.关于如何实现这一点还有很多话要说,我只是想为您指出一个可能的方向。

First, there is nothing really wrong with your general approach.首先,您的一般方法没有什么问题。

Having said that, there are some errors.话虽如此,还是有一些错误。

  • You are referencing your hashMap as a static value via Restaurant where it isn't declared static.您通过未声明为静态的 Restaurant 将您的 hashMap 引用为静态值。
  • you are calling isValid() from within a static context (Main) but the method is not declared static.您正在从静态上下文(Main)中调用isValid() ) 但该方法未声明为静态的。

This is how I might approach it.这就是我可能接近它的方式。 And it's not to say it is the best approach or style either.这也不是说它是最好的方法或风格。

  • I chose to use an enum to hold details about each menu item, and a class to process the order.我选择使用enum来保存每个菜单项的详细信息,并使用一个类来处理订单。 Enum's easily lend themselves to this type of requirement. Enum 很容易满足这种类型的要求。 I recommend you read up on them (they are too involved to explain them in detail here).我建议您阅读它们(它们过于复杂,无法在此处详细解释)。 But because they are static they are best served in holding values that are not likely to change.但是因为它们是static的,所以它们最好用于持有不太可能改变的值。

  • Each item has two arguments are are processed in the enum's constructor.每个项目都有两个参数,都在enum's构造函数中处理。

  • so each item has a separate range of its item (the ingredient)所以每个项目都有一个单独的项目范围(成分)

  • the enum also has a validate method to check to see if a supplied count of items meets the requirements. enum还有一个 validate 方法来检查提供的项目计数是否符合要求。

The actual order is in the MyOrder class.实际订单在MyOrder类中。

  • it contains a map to hold the counts of each ingredient.它包含一个地图来保存每种成分的数量。
  • an add method to add the current quantity to the map for a given ingredient. add 方法将当前数量添加到给定成分的地图中。
  • and a display method to print informative information about the order.以及打印订单信息的显示方法。
enum Menu {MEAT(0,1), BREAD(1,1), CHEESE(1,1), EXTRAS(1,3), SAUCES(1,3);
    private int min;
    private int max;
    private Menu(int min, int max) {
        this.min = min;
        this.max = max;
    }
    
    public int getMax() {
        return max;
    }
    public int getMin() {
        return min;
    }
    
    public  boolean validate(int count) {
        return count >= min && count <= max;
    }
}
class MyOrder {
    private EnumMap<Menu, Integer> counts = new EnumMap<>(Menu.class);
    
    public void display() {
        for (Menu item : Menu.values()) {
            int count = counts.get(item);
        System.out.printf("%-7s: %d  (%d,%d) %s%n",item.name(), count, item.getMin(), item.getMax(),
                item.validate(count) ? "" : "Item count out of range.");
        }
    }
        
    public boolean add(Menu item, int quantity) {
        return item.validate(counts.merge(item, quantity, Integer::sum));
    }
}
        
public class Restaurant {

    public static void main(String[] args) {
        boolean isValid;
        MyOrder order = new MyOrder();
        isValid =  order.add(Menu.BREAD,2);
        isValid &= order.add(Menu.MEAT,1);
        isValid &= order.add(Menu.CHEESE,2);
        isValid &= order.add(Menu.EXTRAS,3);
        isValid &= order.add(Menu.SAUCES,2);
        if (isValid) {
            System.out.println("Your order is accepted.");
        } else {
            System.out.println("Order is not in compliance");
            order.display();
        }
    }
}

prints印刷

Order is not in compliance
MEAT   : 1  (0,1) 
BREAD  : 1  (1,1) 
CHEESE : 2  (1,1) Item count out of range.
EXTRAS : 3  (1,3) 
SAUCES : 2  (1,3) 

Also remember that the result of any if statement is a boolean .还要记住,任何if statement的结果都是boolean So the inequality can be assigned to a boolean and then tested later (if it makes sense to do so).因此,可以将inequality分配给布尔值,然后稍后进行测试(如果这样做有意义的话)。 Also notice that I don't check for a legitimate order until the end.另请注意,我直到最后才检查合法订单。 Some might prefer to signal a bad order as soon as it occurs.有些人可能更愿意在订单出现错误时立即发出信号。 This is a design choice.这是一种设计选择。

For more information check.欲了解更多信息检查。
Enum 枚举
EnumMap 枚举映射
Map.merge 地图合并

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM