说明:


鲍勃是一个缺乏狂热的少年。在对话中,他的回答非常有限。

Bob回答“肯定”。如果您问他一个问题。

他回答“哇,放松一下!”如果你对他大吼。

他说'好。那样吧!如果您没有真正对他说话就


他回答“随便”。


代码:

import java.util.Optional;

enum SentenceType {
  Question, Yell, Silence, Misc
}

public class Bob {
  public String hey(String sentence) {
    String trimmedSentence = Optional.ofNullable(sentence).orElse("").trim();
    SentenceType classifiedSentence = classify(trimmedSentence);

    if (classifiedSentence == SentenceType.Silence) {
      return "Fine. Be that way!";
    } else if (classifiedSentence == SentenceType.Question) {
      return "Sure.";
    } else if (classifiedSentence == SentenceType.Yell) {
      return "Whoa, chill out!";
    }
    return "Whatever.";

  }

  private SentenceType classify(String sentence) {
    if (sentence.isEmpty()) {
      return SentenceType.Silence;
    } else if (isAllLettersUpperCase(sentence)) {
      return SentenceType.Yell;
    } else if (isQuestion(sentence)) {
      return SentenceType.Question;
    }
    return SentenceType.Misc;
  }

  private boolean isAllLettersUpperCase(String sentence) {
    char[] charArray = sentence.toCharArray();
    boolean hasLetters = false;
    for (char ch : charArray) {
      if (Character.isLetter(ch)) {
        hasLetters = true;
        if (Character.isLowerCase(ch)) {
          return false;
        }
      }
    }
    return hasLetters && true;
  }

  private boolean isQuestion(String sentence) {
    return (sentence.charAt(sentence.length() - 1) == '?') ? true : false;
  }
}


测试套件:

import org.junit.Test;

import static org.junit.Assert.*;

public class BobTest {
    private final Bob bob = new Bob();

    @Test
    public void saySomething() {
        assertEquals(
            "Whatever.",
            bob.hey("Tom-ay-to, tom-aaaah-to.")
        );
    }

    @Test
    public void shouting() {
        assertEquals(
            "Whoa, chill out!",
            bob.hey("WATCH OUT!")
        );
    }

    @Test
    public void askingAQuestion() {
        assertEquals(
            "Sure.",
            bob.hey("Does this cryogenic chamber make me look fat?")
        );
    }

    @Test
    public void askingANumericQuestion() {
        assertEquals(
            "Sure.",
            bob.hey("You are, what, like 15?")
        );
    }

    @Test
    public void talkingForcefully() {
        assertEquals(
            "Whatever.",
            bob.hey("Let's go make out behind the gym!")
        );
    }

    @Test
    public void usingAcronymsInRegularSpeech() {
        assertEquals(
            "Whatever.", bob.hey("It's OK if you don't want to go to the DMV.")
        );
    }

    @Test
    public void forcefulQuestions() {
        assertEquals(
            "Whoa, chill out!", bob.hey("WHAT THE HELL WERE YOU THINKING?")
        );
    }

    @Test
    public void shoutingNumbers() {
        assertEquals(
            "Whoa, chill out!", bob.hey("1, 2, 3 GO!")
        );
    }

    @Test
    public void onlyNumbers() {
        assertEquals(
            "Whatever.", bob.hey("1, 2, 3")
        );
    }

    @Test
    public void questionWithOnlyNumbers() {
        assertEquals(
            "Sure.", bob.hey("4?")
        );
    }

    @Test
    public void shoutingWithSpecialCharacters() {
        assertEquals(
            "Whoa, chill out!", bob.hey("ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!")
        );
    }

    @Test
    public void shoutingWithUmlauts() {
        assertEquals(
            "Whoa, chill out!", bob.hey("\u00dcML\u00c4\u00dcTS!")
        );
    }

    @Test
    public void calmlySpeakingWithUmlauts() {
        assertEquals(
            "Whatever.", bob.hey("\u00dcML\u00e4\u00dcTS!")
        );
    }

    @Test
    public void shoutingWithNoExclamationMark() {
        assertEquals(
            "Whoa, chill out!", bob.hey("I HATE YOU")
        );
    }

    @Test
    public void statementContainingQuestionMark() {
        assertEquals(
            "Whatever.", bob.hey("Ending with ? means a question.")
        );
    }

    @Test
    public void prattlingOn() {
        assertEquals(
            "Sure.", bob.hey("Wait! Hang on. Are you going to be OK?")
        );
    }

    @Test
    public void silence() {
        assertEquals(
            "Fine. Be that way!", bob.hey("")
        );
    }

    @Test
    public void prolongedSilence() {
        assertEquals(
            "Fine. Be that way!", bob.hey("    ")
        );
    }
}


问题


起初我对unicode问题表示怀疑,但我认为库可以很好地处理大小写unicode。
我花了很多时间来编写看起来很简单isAllLettersUpperCase我仍然怀疑我写的很好,并且可以处理所有极端情况,如何更自信地编写这样的方法?更改我会中断测试的顺序。希望可以有一个更好的解决方案。
总体思路是尽可能减少耦合,希望我可以通过使用枚举在某种程度上解决耦合问题。

PS:了解更多详细信息请参见此处。

评论

不知道我是否想念什么,所以我发表评论。句子.equals(sentence.toUpperCase())怎么样?

@Malachi docs.oracle.com/javase/7/docs/api/java/lang / ...

duh .... ROFL我讨厌星期一,@ Bene。 Character.isUpperCase()接受单个字符。

是的,我只是问String.toUpperCase()是否可以用于他的isAllLetterUpperCase()方法。还是会返回与当前方法不同的结果?

O_O用我的名字和头像...这不是巧合吗?

#1 楼

避免使用带有枚举的if

您正在对Bob的所有可能响应使用枚举。这是个好主意,您可以再进一步。当前,您具有:

SentenceType classifiedSentence = classify(trimmedSentence);

if (classifiedSentence == SentenceType.Silence) {
    return "Fine. Be that way!";
} else if (classifiedSentence == SentenceType.Question) {
    return "Sure.";
} else if (classifiedSentence == SentenceType.Yell) {
    return "Whoa, chill out!";
}
return "Whatever.";


,其中包含多个if语句,以便返回正确的输出。

考虑将该句子作为属性枚举的形式:

enum SentenceType {
    Question("Sure."),
    Yell("Whoa, chill out!"),
    Silence("Fine. Be that way!"),
    Misc("Whatever.");

    private final String sentence;

    SentenceType(String sentence) {
        this.sentence = sentence;
    }

    public String getSentence() {
        return sentence;
    }
}


这样,您可以重构此块:

SentenceType classifiedSentence = classify(trimmedSentence);
return classifiedSentence.getSentence();


而没有任何if s。


关于方法顺序的问题,我认为这不是问题:它仅取决于顺序,因为您决定早些写条件,返回。这意味着确定鲍勃是否在吼叫的检查是预先假设鲍勃实际上在说什么,这也意味着您没有明确检查鲍勃在说什么以确定他是否在吼叫。这确实使订单成对,但是确实简化了所涉及的检查:否则,您需要为每个条件添加一个!sentence.isEmpty() && ...

可选

您正在使用Optional作为以下:

String trimmedSentence = Optional.ofNullable(sentence).orElse("").trim();


这是过分的,仅使用三元运算符会更简单(也许更清楚):

String trimmedSentence = sentence == null ? "" : sentence.trim();


流API

您可以将isAllLettersUpperCase方法重构为以下内容

private boolean isAllLettersUpperCase(String sentence) {
    return sentence.chars().anyMatch(Character::isLetter) && 
            sentence.chars().filter(Character::isLetter).allMatch(Character::isUpperCase);
}


,这样可能更易于阅读:如果至少有一个字母且所有字母均为大写,则返回true

评论


\ $ \ begingroup \ $
我真的很喜欢增加枚举的想法。
\ $ \ endgroup \ $
– CodeYogi
16年5月2日在18:06

\ $ \ begingroup \ $
您不能为isAllLettersUpperCase返回值做条件的后半部分吗?
\ $ \ endgroup \ $
–马拉奇♦
16年5月2日在18:23

\ $ \ begingroup \ $
@Malachi不,如果没有元素,则allMatch将返回true,因此,如果没有前半部分,则没有字母时将返回true(请参阅此处)。
\ $ \ endgroup \ $
–Tunaki
16年5月2日在18:56



\ $ \ begingroup \ $
太好了,谢谢。我对很多Java工具都不熟悉。
\ $ \ endgroup \ $
–马拉奇♦
16年5月2日在18:58

\ $ \ begingroup \ $
@CodeYogi我没有添加它(因为它可能太多了),但是您也可以通过在Predicate 中为每个枚举添加每个检查来重构classify方法。
\ $ \ endgroup \ $
–Tunaki
16年5月2日在19:30

#2 楼


  private boolean isQuestion(String sentence) {
    return (sentence.charAt(sentence.length() - 1) == '?') ? true : false;
  }



在这里,您实际上可以编写得更简单一些,而无需三进制。

return sentence.charAt(sentence.length() - 1) == '?';


您要返回true或false才能返回true或false。只需执行一次。


您可以减少此上的压痕数量


  private boolean isAllLettersUpperCase(String sentence) {
    char[] charArray = sentence.toCharArray();
    boolean hasLetters = false;
    for (char ch : charArray) {
      if (Character.isLetter(ch)) {
        hasLetters = true;
        if (Character.isLowerCase(ch)) {
          return false;
        }
      }
    }
    return hasLetters && true;
  }



我会将方法名称更改为areAllLettersUpperCase(听起来更好)。在这种方法中,对

return hasLetters && true;

没什么意义,因为它确实出现在

return hasLetters;


/>我会将has letters功能分离到自己的方法中,这样,如果没有字母,我就可以完全做其他事情。

private boolean hasLetters(String sentence) {
    char[] charArray = sentence.toCharArray();
    for (char ch : charArray) {
        if (Character.isLetter(ch)) return true;
    }
    return false;
}


,您可以像这样调用它这是在areAllLettersUpperCase方法中实现的。在此方法之外,并相应地重命名。

通过分离句子中字母的检查,您将遵循单一职责原则(SRP)。每种方法都应该做一件事。调用类在调用所有CAPS中的句子检查之前应先调用不存在字母的检查。

SRP也是编写SOLID Code之一。

评论


\ $ \ begingroup \ $
facepalm!我总是忘记这一点。
\ $ \ endgroup \ $
– CodeYogi
16年5月2日在17:33

\ $ \ begingroup \ $
@CodeYogi,哈哈,我一直都在做!
\ $ \ endgroup \ $
–马拉奇♦
16年5月2日在17:55

\ $ \ begingroup \ $
我想制作一个类似HasLetters的单独方法,但是该方法需要再次遍历整个字符串,这对性能没有影响吗?
\ $ \ endgroup \ $
– CodeYogi
16年5月2日在18:04

\ $ \ begingroup \ $
如果您读了一整本书,是的,但是对于普通的来回对话,您不会注意到,除非您同时运行数千个实例来测试性能问题。
\ $ \ endgroup \ $
–马拉奇♦
16年5月2日在18:21

\ $ \ begingroup \ $
@Malachi是的,这就是我的想法:)
\ $ \ endgroup \ $
–Tunaki
16年5月2日在19:08

#3 楼

只是为了补充其他答案:

  public String hey(String sentence) {
    String trimmedSentence = Optional.ofNullable(sentence).orElse("").trim();
    SentenceType classifiedSentence = classify(trimmedSentence);

    if (classifiedSentence == SentenceType.Silence) {
      return "Fine. Be that way!";
    } else if (classifiedSentence == SentenceType.Question) {
      return "Sure.";
    } else if (classifiedSentence == SentenceType.Yell) {
      return "Whoa, chill out!";
    }
    return "Whatever.";
  }


您可以将此if-elseif-else序列写为switch块:

    switch (classifiedSentence) {
    case Silence:
      return "Fine. Be that way!";
    case Question:
      return "Sure.";
    case Yell:
      return "Whoa, chill out!";
    default:
      return "Whatever.";
    }


这样可以为您(和您的其他读者)省下几句话。

代替这个:

  private boolean isQuestion(String sentence) {
    return (sentence.charAt(sentence.length() - 1) == '?') ? true : false;
  }


只写这个:

  private boolean isQuestion(String sentence) {
    return sentence.endsWith("?");
  }


评论


\ $ \ begingroup \ $
我尝试了switch,但出现错误,表示表达式必须解析为int。
\ $ \ endgroup \ $
– CodeYogi
16年3月3日,3:30

\ $ \ begingroup \ $
@CodeYogi:从Java 1.5开始,switch语句接受枚举,从Java 1.7开始,它也接受字符串。请不要告诉我您正在以Java 1.4模式进行开发。
\ $ \ endgroup \ $
–罗兰·伊利格(Roland Illig)
16年5月3日在20:14

\ $ \ begingroup \ $
不,它的Java 7,也许我做错了。
\ $ \ endgroup \ $
– CodeYogi
16年5月4日在2:28