最近,我从一系列看起来很有希望的面试中被拒绝了。我在问卷调查样式的审查中做得很好,然后他们把这份任务(或多或少)交给了我:尽管添加新饮料相对容易,但是每种饮料的配方初始化都应进行硬编码。机器应该在启动时以及每一次有效的用户输入之后显示原料库存(+成本)和菜单。饮料成本取决于成分的组合。例如,咖啡是3单位咖啡(每份75美分),1单位糖(每份25美分),1单位奶油(每份25美分)。配料和菜单项应按字母顺序打印。如果饮料缺货,则应相应打印。如果该饮料有库存,则应打印“配药:”。要选择一种饮料,用户应该输入一个相关的数字。如果他们提交“ r”或“ R”,则应重新补充成分,而“ q”或“ Q”应退出。空行应被忽略,无效的输入应显示无效的输入消息。


它们提供了默认成分(&stock @ 10)和饮料/食谱。 >他们告诉我“不要试图打动”,不要过多地记录文件(特别是不需要评论)。该代码应具有可伸缩性,可读性,并且不应过于复杂。

无论如何,我很可能在数据结构选择或处理饮料选择方面犯了一些重大错误,而不是缺少一些细节。不过,如果有人好奇,我可以私下发送完整的职位描述...我只是不想把它放在那儿,因为这是一家正在招聘的大公司。我在代码中更改了某些内容的名称,以使确切的赋值难于搜索。实际上,我对要在此代码中进行更改的事情有很多意见,但是我很难相信它们是会让我被拒绝的事情。

(从那以后,我被告知团队希望“更多的OO设计”……感觉就像我个人朝这个方向发展。 br />
DrinkMachine

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;

public class DrinkMachine{

    private static List<Drink> drinkList = new ArrayList<Drink>();
    private static List<Ingredient> ingredientList = new ArrayList<Ingredient>();

    public static void main(String[] args) {
        addAllIngredients();
        addAllDrinks();
        updateCosts();
        updateMakeable();
        display();
        startIO();
    }

    public static void startIO(){
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String input = "";
        //running loop
        while(true){            
            try {
                /*I considered using a switch here for more readable code. Elected to have a dynamic drink menu so drinks can be added
                 * to addAllDrinks() and this loop would not have to be change. The decision to have slightly less readable code
                 * in favor of a dynamic menu was made upon my belief that easily modifiable code is incredibly important.
                 */
                input = reader.readLine().toLowerCase();

                if(input.equals("")){
                    continue;
                }else if (input.equals("q")){ 
                    System.exit(0); 
                }else if(input.equals("r")){ 
                    restockIngredients();
                    updateMakeable();
                }else if(Integer.parseInt(input) > 0 && Integer.parseInt(input) <= drinkList.size()){ //dynamic drink menu selection
                    makeDrink(drinkList.get(Integer.parseInt(input)-1));
                }else{
                    throw new IOException();//legal, but invalid input
                }
            } catch (Exception e) {
                System.out.println("Invalid selection: " + input + "\n");//illegal input
            }
        }//running loop     
    }

    public static void display(){
        //The sample IO in the assignment appears to add a blank line after output, so I included that.
        System.out.println("Inventory:\n");
        for (Ingredient i : ingredientList){
            System.out.println(i.getName() + "," + i.getStock() + "\n");
        }

        System.out.println("Menu:\n");
        int count = 1;
        for (Drink d : drinkList){
            System.out.printf("%d,%s,$%.2f," + d.getMakeable() + "\n\n", count, d.getName(), d.getCost());
            count++;
        }
    }

    public static void updateMakeable(){
        for (Drink d : drinkList){
            Map<String, Integer> currRecipe = d.getRecipe();
            for (Ingredient i : ingredientList){
                if (currRecipe.containsKey(i.getName()) && i.getStock() < currRecipe.get(i.getName())){
                    d.setMakeable(false);
                    break;//check next drink
                }
                d.setMakeable(true);
            }//Ingredient loop
        }//Drink loop
    }

    public static void updateCosts(){
        for (Drink d : drinkList){
            double currCost = 0;
            Map<String, Integer> currRecipe = d.getRecipe();
            for (Ingredient i : ingredientList){
                if (currRecipe.containsKey(i.getName())){
                    currCost += i.getCost()*currRecipe.get(i.getName());
                }
            }//inner
            d.setCost(currCost);
        }//outer
    }

    public static void makeDrink(Drink drink){
        if(drink.getMakeable()){
            System.out.println("Dispensing: " + drink.getName() + "\n");
            for (Ingredient i : ingredientList){
                if(drink.getRecipe().containsKey(i.getName())){
                    i.setStock(i.getStock()-drink.getRecipe().get(i.getName()));
                }
            }
        }else{
            System.out.println("Out of stock: " + drink.getName() + "\n");
        }
        updateMakeable();
        display();
    }

    public static void restockIngredients(){
        for(Ingredient i : ingredientList){
            i.setStock(10);
        }
        updateMakeable();
        display();
    }

    //Add ingredients through addAllIngredients
    public static void addIngredient(Ingredient ingredient){
        ingredientList.add(ingredient);
    }
    //Add drinks through addAllDrinks
    public static void addDrink(String name, String[] recipe){
        drinkList.add(new Drink(name, recipe));
    }

    public static void addAllIngredients(){
        addIngredient(new Ingredient("Coffee", 0.75));
        addIngredient(new Ingredient("Decaf Coffee", 0.75));
        addIngredient(new Ingredient("Sugar", 0.25));
        addIngredient(new Ingredient("Cream", 0.25));
        addIngredient(new Ingredient("Steamed Milk", 0.35));
        addIngredient(new Ingredient("Foamed Milk", 0.35));
        addIngredient(new Ingredient("Espresso", 1.10));
        addIngredient(new Ingredient("Cocoa", 0.90));
        addIngredient(new Ingredient("Whipped Cream", 1.00));   

        Collections.sort(ingredientList);
    }

    public static void addAllDrinks(){  
        addDrink("Coffee", new String[]{"Coffee", "Coffee", "Coffee", "Sugar", "Cream"});
        addDrink("Decaf Coffee", new String[]{"Decaf Coffee", "Decaf Coffee", "Decaf Coffee", "Sugar", "Cream"});
        addDrink("Caffe Latte", new String[]{"Espresso", "Espresso", "Steamed Milk"});
        addDrink("Caffe Americano", new String[]{"Espresso", "Espresso", "Espresso"});
        addDrink("Caffe Mocha", new String[]{"Espresso", "Cocoa", "Steamed Milk", "Whipped Cream"});
        addDrink("Cappuccino", new String[]{"Espresso", "Espresso", "Steamed Milk", "Foamed Milk"});

        Collections.sort(drinkList);
    }

}


Drink

import java.util.HashMap;
import java.util.Map;

public class Drink implements Comparable<Drink>{
    private Map<String, Integer> recipe = new HashMap<String, Integer>();//map ingredients to units per
    private String name;
    private double totalCost = 0;
    private boolean makeable = false;

    public Drink(String name, String[] recipe){
        this.name = name;
        setRecipe(recipe);
    }

    public int compareTo(Drink drink){
        return name.compareTo(drink.getName());
    }

    public void setRecipe(String[] recipe){
        for(String s : recipe){
            if(this.recipe.containsKey(s)){
                this.recipe.put(s, this.recipe.get(s)+1);//increment if multiple units
            }else{
                this.recipe.put(s, 1);//insert first occurrence of ingredient
            }
        }
    }

    public void setName(String name){
        this.name = name;
    }

    public void setCost(double totalCost){
        this.totalCost = totalCost;
    }

    public void setMakeable(boolean makeable){
        this.makeable = makeable;
    }

    public Map<String, Integer> getRecipe(){
        return recipe;
    }

    public double getCost(){
        return totalCost;
    }

    public String getName(){
        return name;
    }

    public boolean getMakeable(){
        return makeable;
    }

}


Ingredient

    public class Ingredient implements Comparable<Ingredient>{
        private String name = "";
        private double cost = 0.00;
        private int stock = 0;

        public Ingredient(String name, double cost){
            this.name = name;
            this.cost = cost;
            this.stock = 10;
        }

        public int compareTo(Ingredient ingredient) {
            return name.compareTo(ingredient.getName());
        }

        public void setName(String name){
            this.name = name;
        }

        public void setCost(double cost){
            this.cost = cost;
        }

        public void setStock(int stock){
            this.stock = stock;
        }

        public String getName(){
            return name;
        }

        public double getCost(){
            return cost;
        }

        public int getStock(){
            return stock;
        }

    }


评论

如果我没有审查实现,如果我正在审查此代码以进行采访,那么所有静态成员和静态void方法真的会让我感到恐惧。大多数静态状态(大多数是可变状态)使测试变得困难,并损害了可扩展性。

另外,为什么addAll *方法是公共的?它们仅在初始化时使用一次。作为一般建议:OOP指示您需要尽可能少地了解类的实现。不应有一个公共的update *方法(如果有的话),因为一个类的对象应该期望其行为正确并在需要更新时自行确定。看来您将OOP看作是“让我们将程序拆分为有用的子例程”,而不是“我想在不知道其具体实现的情况下使用我的类”。

#1 楼


从那以后,我被告知团队希望“一个更多的OO设计” ...感觉就像我个人朝这个方向发展一样。


所有方法在您的DrinkMachine类中是静态的。我相信拥有多个DrinkMachine的能力是非常重要的功能。 DrinkMachines应该可以具有不同的库存,不同的产品等。现在,您可能会认为“但是我被告知要保持简单!”。这不是关于“试图打动”或任何事情,而是关于合理的功能。 >

创建static之后,真的需要更改它的名称吗?这也有助于蒂姆(Tim)的观点:如果您在构造函数中分配字段,则无需初始化字段,这很令人困惑。 -DrinkMachine字段只能初始化一次。

评论


\ $ \ begingroup \ $
我认为,拥有一个以上DrinkMachine功能是一项重要功能是正确的,即使这不是特定要求。关于最终确定某些字段...我想我需要更加认真地考虑是否应该更改字段。我想我经常对此说“为什么不”或“不伤害”……换句话说,我决定不将它们定型,因为我想不出为什么他们不应该改变。相反,我认为最好考虑一下“是否有理由改变它?”如果我想不到的话,最后宣布。
\ $ \ endgroup \ $
– spacecadet
2015年3月4日在8:57

\ $ \ begingroup \ $
@spacecadet:该主题包含可变/不可变之类的关键字。作为太多的程序员,您被教导构建自动可变的对象,这很可悲。尝试了解不可变的优势:programmers.stackexchange.com/questions/151733/…
\ $ \ endgroup \ $
–西里尔·甘登(Cyril Gandon)
2015年3月5日13:24

\ $ \ begingroup \ $
@CyrilGandon这很棘手,因为我知道我通过使所有内容都可变而过分了,但是当涉及到可变性时,我的很多感受是这样的: 170331我问:“为什么不更改?”因为可变性是默认设置。问题应该改为“为什么要更改?”那么我们希望事情是最终的,并且默认情况下是不变的。无论如何,我不是一个决定这种思想流派是对还是错的人,但这就是为什么我(显然还有其他人)认为可变性,直到有明确的理由不这样做的原因。
\ $ \ endgroup \ $
– spacecadet
2015年5月5日23:46

#2 楼

首先,很抱歉您没有得到这份工作。下次好运了:)

总的来说,我认为您的代码不错。但是我有两点:

OOP


我主要的抱怨是您缺少食谱课。将其作为字符串数组/映射不是很灵活。因此,您还将饮料类的内部工作方式暴露给外界(例如,在updateMakeable中)。

setRecipe有点令人困惑。通常,设置员只能设置东西,但是此方法可以完成更多工作。它也不适用于getRecipe,它返回的类型不同于setRecipe的类型。它显示,读取输入,处理机器的状态和逻辑,并且包含测试用例和主循环。至少前两种情况和后两种情况应该在其他地方处理。

其他


如果在中分配字段,则无需初始化字段一个构造函数,这只是令人困惑。在Java中,显示循环结束位置的注释不是很常见(如果您的代码如此嵌套以至于需要它们,则应该对其进行重构)。
实际上不需要许多注释,或者应该将其更好地集成代码(例如,DrinkMachine不应该是注释,但是异常消息,@Override并不是很有帮助,等等)。
短的变量名导致难以阅读的代码(legal, but invalid input//running loopdrink全部不太长,并且比ingredientcurrentCostd更具可读性)。


评论


\ $ \ begingroup \ $
谢谢,这里有很多好东西。食谱课似乎很重要。我可能从字面上理解了他们的一些技巧(简单,不要尝试打动别人,等等)。我可以回想起更多地拆分代码的想法,但是告诉自己,在太多不同的地方放置太多代码将与简单性背道而驰……我想这最终会产生相反的效果。再次感谢。
\ $ \ endgroup \ $
– spacecadet
2015年3月3日在22:43

\ $ \ begingroup \ $
我不需要引入Recipe类。毕竟,什么是饮料,但名称和成分表呢?问题在于,Drink不应携带可煮性之类的咖啡机状态,而Ingredients不应携带库存水平之类的咖啡机状态。
\ $ \ endgroup \ $
– 200_success
2015年3月4日在12:07

#3 楼

我同意他们的评估,即可以改进OOP设计。特别是,初始化数据的方式很麻烦,我反对在整个updateMakeable()代码中调用DrinkMachine的必要性。到另一个?是按名称,成本还是数量进行比较?我认为,stock应该是无状态且不可变的。 Ingredient没有任何意义。 DrinkMachine对于本练习可能是过度设计的。

由于该练习表明对要素进行硬编码是可以的,因此我只使用按字母顺序输入的枚举。

import java.math.BigDecimal;

public enum Ingredient {
    // An extensible design wouldn't use an enum, but the problem specification
    // allows such hard-coding.  These entries must be in alphabetical order.
    COCOA("0.90"),
    COFFEE("0.75"),
    CREAM("0.25"),
    DECAF_COFFEE("0.75"),
    ESPRESSO("1.10"),
    FOAMED_MILK("0.35"),
    STEAMED_MILK("0.35"),
    SUGAR("0.25"),
    WHIPPED_CREAM("1.00");

    private final String name;
    private final BigDecimal cost;

    Ingredient(String cost) {
        this.name = this.name().replace("_", " ").toLowerCase();
        this.cost = new BigDecimal(cost);
    }

    // Using fixed-point representation to prevent rounding problems
    public BigDecimal getCost() {
        return this.cost;
    }

    public String toString() {
        return this.name;
    }
}


Drink.java

Ingredient的许多相同之处也适用于setName()。饮料本身不是setCost()。您无需计划产品重新设计(Ingredient),重新命名(Drink)或调整利润率(Comparable)。可扩展性是setRecipe()状态的一部分。 (如果要使用这种方法,则由于拼写和谓词的Java命名约定,setName()setCost()更合适的名称。)

DrinkMachine中,它应该是返回一个不可修改的映射的一个好主意。 />DrinkMachine.java

上述改进将在这里得到回报:



isMakable()getMakeable()由枚举负责。 />
getRecipe()addAllIngredients()构造函数负责。

不再需要addAllDrinks(),因为可饮用性不再是每种饮料的特性。而是,机器可以查询自身以查看是否可以制作每种饮料。 (请参阅updateCosts()。)消除Drink是一件好事-需要在任何地方调用它都是容易出错的。请注意,在处理updateMakeable()命令时,先调用canMake(),然后再调用updateMakeable(),但是"r"本身已经调用了restockIngredients();。它可以接管您的updateMakeable();方法。

restockIngredients()提供的接口比updateMakeable()更好。在这种情况下,由于您只需要DrinkMachinetoString()也可以很好地工作。

现在已经完成了初始化工作,因此事件循环可以位于display()中。在循环中,调用Scanner是不合理的-仅BufferedReader就足够了。捕捉所有readLine()也太强大了。例如,文件结尾(在Windows上为Ctrl z,在其他位置为Ctrl d)将无法导致程序退出我期望的方式。您也将合法但无效的输入归类为Console而不是main(),这很奇怪。

import java.math.BigDecimal;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public enum Drink {
    // An extensible design wouldn't use an enum, but the problem specification
    // allows such hard-coding.  These entries must be in alphabetical order.
    CAFFE_AMERICANO("Caffè Americano", "3 ESPRESSO"),
    CAFFE_LATTE("Caffè Latte", "2 ESPRESSO", "STEAMED_MILK"),
    CAFFE_MOCHA("Caffè Mocha", "ESPRESSO", "COCOA", "STEAMED_MILK", "WHIPPED_CREAM"),
    CAPPUCCINO("Cappuccino", "2 ESPRESSO", "STEAMED_MILK", "FOAMED_MILK"),
    COFFEE("Coffee", "3 COFFEE", "SUGAR", "CREAM"),
    DECAF_COFFEE("Decaf Coffee", "3 DECAF_COFFEE", "SUGAR", "CREAM");

    private final String name;
    private final Map<Ingredient, Integer> ingredients;
    private final BigDecimal cost;

    Drink(String name, String... recipe) {
        Map<Ingredient, Integer> map = new HashMap<Ingredient, Integer>();
        BigDecimal cost = BigDecimal.ZERO;
        for (String spec : recipe) {
            String[] amountOfStuff = spec.split(" ", 2);
            int quantity = (amountOfStuff.length > 1) ? Integer.parseInt(amountOfStuff[0]) : 1;
            String stuff = amountOfStuff[amountOfStuff.length - 1];
            Ingredient ingredient = Enum.valueOf(Ingredient.class, stuff);

            map.put(ingredient, quantity);
            cost = cost.add(ingredient.getCost().multiply(new BigDecimal(quantity)));
        }
        this.name = name;
        this.ingredients = Collections.unmodifiableMap(map);
        this.cost = cost;
    }

    public Map<Ingredient, Integer> getRecipe() {
        return this.ingredients;
    }

    public BigDecimal getPrice() {
        return this.cost;
    }

    public String toString() {
        return this.name;
    }
}


评论


\ $ \ begingroup \ $
我真的很喜欢您的枚举方法,我发现您的代码总体上可读性强(+1)。只是一个简单的问题:如果您担心舍入,您是否有理由使用BigDecimal而不是简单的double或float或什至int的价格?我问的原因是因为@skiwi复制了您的想法codereview.stackexchange.com/questions/83195/…,对于这样一个非常简单的目的,使用BigDecimal之类的非常复杂的对象的使用可能会有所帮助。
\ $ \ endgroup \ $
– Markus A.
2015年3月4日在21:44



\ $ \ begingroup \ $
@MarkusA。此应用程序对性能不敏感。与使用整数美分相比,BigDecimal生成的代码更简单。 float和double无法精确表示百分之一。 BigDecimal不受舍入错误的影响。它们带有精度信息,因此新的BigDecimal(“ 1.00”)自然会打印为“ 1.00”而不是“ 1”。与字符串不同,您可以对它们进行算术运算,这是达到总成本所必需的。由于所有这些原因,我选择了BigDecimal。
\ $ \ endgroup \ $
– 200_success
2015年3月4日在23:03

\ $ \ begingroup \ $
我不知道它们在打印时会保持精度。确实有帮助。但是,浮点数和双精度词对数字的不精确性非常小,以至于您必须在同一张支票上订购数万种饮料,然后才能看到浮点数。要翻倍,就需要数万亿美元。应该是安全的。 :)
\ $ \ endgroup \ $
– Markus A.
2015年3月5日,下午3:59

#4 楼

我相信您的访问员正在寻找装饰器模式的用法。
至少,我在阅读的设计模式书中已经看到几乎完全相同的问题描述。

装饰器模式使您可以从普通的咖啡开始动态创建构图,并在其上添加装饰层(牛奶,糖等)。 。

调用最高层的cost函数,然后将计算最高层的成本,并调用其下一层的cost函数,将两者加在一起。简短的草图:

base = new Drink(); 
DrinkInterface topping1 = new Topping(base);
DrinkInterface topping2 = new Topping(topping1);


我相信为您提供完整的解决方案是没有意义的,使用模式重试将是一个不错的练习!

有关装饰器模式(咖啡制作场景)的更深入说明,请看一下。

评论


\ $ \ begingroup \ $
我个人认为,避免这种模式是面试官不要试图打动的意思。
\ $ \ endgroup \ $
–西蒙·福斯伯格
2015年3月4日在13:15

\ $ \ begingroup \ $
我只是不知道原因。列出所有成分的清单对我来说似乎更简单。在这种情况下,使用您建议的模式将导致很多类。
\ $ \ endgroup \ $
–西蒙·福斯伯格
15年3月4日在15:20

\ $ \ begingroup \ $
使逻辑分开吗?因为应该以需要附加逻辑的想法来发展。如果要添加一个新的装饰器,您要做的就是创建一个实现DrinkInterface的类,并且可以完美缩放,而无需更改任何其他现有类(当然,客户除外)。还要想像一下,如果他们将来想添加make()方法,则该方法对于每种浇头(牛奶/糖/摩卡)可能会有所不同。我并不是说这种模式是必需品或金弹。但是,我确实认为,使其成为面向未来的方法是一个好主意,并且更易于沟通。
\ $ \ endgroup \ $
– DJanssens
2015年3月4日15:35

\ $ \ begingroup \ $
我回避这种方法的一个小原因是装饰器模式暗示了一个顺序:先加牛奶再加糖,再加糖然后再加牛奶都没关系。直接与饮料互动比较麻烦,因为它被埋藏在参考层之下。
\ $ \ endgroup \ $
–歪斜
15年3月4日在22:55

\ $ \ begingroup \ $
是的-正如DJanssens所建议的,它的Decorator模式是解决此问题的关键...有趣的是Wikipedia作为示例“制作咖啡的场景”,实际上可能是该任务的灵感:-):请参见.wikipedia.org / wiki /…
\ $ \ endgroup \ $
– AdamW
2015年5月5日,12:36

#5 楼

很抱歉得知您没有得到这份工作。为您的下一次尝试,我会竭力为您服务。他们希望获得“更多面向对象设计”的回应很可能只是他们提出的一些模糊的东西,以证明他们的决定是正确的。由于很难知道其他应聘者在面试的其他部分的表现,因此甚至不确定这是他们选择其他人的主要原因。
因为这些编码练习是这是公司评估您对特定语言或一般编码的熟悉程度和经验的一种方式,这可能是一堆小东西,使您似乎没有竞争者的经验。 @Tim已经为这些事情整理了一个很好的清单。但是,如果他们专门寻找Java开发人员,那么让我补充一些看法(并非所有人都一定会关注甚至同意):饮料和成分清单。由于这些列表中的每一个都不应该支持两个相同的条目,因此更明智的选择是使用ArrayLists而不是Set。具体来说,使用List将始终保持其条目排序,因此您甚至不再需要使用TreeSet。如果您不使用这些“高级”数据结构类型,可能会认为您不熟悉它们。
由于您仅在Collections.sort(...) -block内部使用了字符串input,因此可以在此处直接定义它(try {}),而不是在外部定义它并将其初始化为空String,即使从未使用该初始化值也是如此。
您将String input = reader...输入错误的输入,通常在读取/写入文件或网络流之类的问题时,这被认为与I / O错误有关。一个更明智的例外是IOExceptionUnsupportedOperationException。但是,总的来说,抛出和捕获异常非常昂贵。因此,更好的方法是将“无效选择” -println移到else块中,而不是引发异常。
通常,捕获通用IllegalArgumentException而不是特定的Exceptions被认为是不好的做法,尤其是当您不要输出异常!例如,如果有人修改了您的makeDrink(...)代码,并导致导致NullPointerException或该代码中抛出其他错误的错误,则您会捕获此异常并将其转换为“无效选择”消息,这将使此操作更加困难来跟踪该错误。
如果for语句或if语句后仅一行,则可以省略花括号(例如for(Ingredient i : ingredientList) i.setStock(10);)。
Java支持varargs,允许您编写public static void addDrink(String name, String... recipe)并使其行为与String[]完全相同,但您现在可以将其称为addDrink("Coffee", "Coffee", "Coffee", "Coffee", "Sugar", "Cream")。尽管这看起来不必添加new String[]{},但它确实使代码不太清晰,因为列表中的第一个"Coffee"得到了特殊处理,但看起来像是调用中列表的一部分。所以这绝对是一个判断电话。
...

他们(或其他人)可能还考虑了其​​他几种对语言(或缺乏语言)熟练程度的指示,例如使用三元运算和枚举(如@ 200_success在他的回答中所做的那样),如果可以找到,甚至可能是通用名称在这个简单的示例中,有一种使之有意义的方法……因此,即使他们明确地说“不要试图给人留下深刻的印象”,但肯定有某些人在生活和呼吸Java,直到他们梦they以求在晚上,它只是本能地做或用来编写简洁明了的代码,而无意“打动”。这些东西到底是什么取决于个人(您可以在此处的答案注释中看到此提示,尽管肯定有一些约定),因此很难说是什么以错误的方式摩擦了您的提交内容(或至少要对其他人有更好的选择。)

请牢记这一点,尽管这对您看看其他人如何编写此代码的示例很有用(@ 200_success很好!),真正提高下次下次面试问题的机会的唯一方法就是编写代码,编写代码,编写代码!或出于一般灵感或如果您对自己的项目缺乏想法,则可以阅读并尝试理解他人为著名的开源项目编写的代码。例如,OpenOffice的大部分是用Java编写的。一定要意识到,并非为这些项目中的任何一个编写的所有代码都将被视为正确执行方法的出色示例。但是它仍然应该为您提供一个很好的方法来找出您可能不了解的语言构造(例如a?b:c)和其他可用的数据结构。因此,您只需尝试理解所读内容的每一个最后(内容和句法)细节,就可以使自己更加足智多谋。

评论


\ $ \ begingroup \ $
我通常同意您在这里的回答,尤其是TreeSet 建议(我自己并未尝试将其视为codereview.stackexchange.com/questions/83195/…)。我会不同意忽略花括号的说法,我会说:永远不要忽略花括号,它们会导致讨厌的错误。
\ $ \ endgroup \ $
–skiwi
15年3月5日,9:54

\ $ \ begingroup \ $
@skiwi是。忽略它们会使代码更易于出错。我将其添加到列表中的原因是,对我而言,在如此简单的上下文中使用它们而不是我放置的单一代码看起来像笨拙的CS类样式的“按规则”编码,而不是多年的现场使用经验。但这是一个高度主观的解释,就良好的编码风格而言,您当然是正确的。对我来说,这有点像在写英语时使用“ kinda”。如果您仅在学校学习过它(因为它不是“正确的”),那么您就不会这样做,但是如果您每天都使用英语,最终您可能会这样做。
\ $ \ endgroup \ $
– Markus A.
2015年5月5日15:21



\ $ \ begingroup \ $
感谢@MarkusA。 ,根据您的建议,我记了很多笔记。几个问题/评论.. re:放大括号。我发现自己经常想要,但是出于可读性的考虑选择保留它们,并且您猜到了,我的教授如何教我保留它。具体来说,设置/树集是我提交后实际上单击的东西。我对许多这样的数据结构很熟悉...我只是不够用。需要更多多样化的项目!是否存在将可变参数视为最佳实践的特定背景?还是个人偏好?如何中断并继续?
\ $ \ endgroup \ $
– spacecadet
2015年5月5日23:16



\ $ \ begingroup \ $
使用break and continue并没有错。在updateMakeable()循环中使用break是正确的。将其更改为if-else而不中断,将是一个逻辑错误。
\ $ \ endgroup \ $
– 200_success
15年7月7日在8:29

\ $ \ begingroup \ $
@spacecadet实际上,我犯了一个错误:我把你的休息读为续篇(我想是菜鸟错误)。我从回答中删除了这一点。我猜想在updateMakeable()方法中引起我注意的是您在内部for循环中反复调用d.setMakeable(true)的事实,而我错误地将其归咎于您使用break。我会将该语句移到“成分循环”之前,这样它只会被调用一次。
\ $ \ endgroup \ $
– Markus A.
2015年3月7日在16:04