|
1 | 1 | # `if` statements |
2 | 2 |
|
3 | 3 | ```java |
| 4 | +import java.util.function.Predicate; |
| 5 | +import java.util.regex.Pattern; |
| 6 | + |
4 | 7 | class Bob { |
5 | | - String hey(String input) { |
6 | | - var inputTrimmed = input.trim(); |
7 | | - |
8 | | - if (isSilent(inputTrimmed)) |
9 | | - return "Fine. Be that way!"; |
10 | | - if (isShouting(inputTrimmed) && isQuestioning(inputTrimmed)) |
11 | | - return "Calm down, I know what I'm doing!"; |
12 | | - if (isShouting(inputTrimmed)) |
13 | | - return "Whoa, chill out!"; |
14 | | - if (isQuestioning(inputTrimmed)) |
| 8 | + |
| 9 | + final private static Pattern isAlpha = Pattern.compile("[a-zA-Z]"); |
| 10 | + final private static Predicate < String > isShout = msg -> isAlpha.matcher(msg).find() && msg == msg.toUpperCase(); |
| 11 | + |
| 12 | + public String hey(String message) { |
| 13 | + var speech = message.trim(); |
| 14 | + if (speech.isEmpty()) { |
| 15 | + return "Fine. Be that way!"; |
| 16 | + } |
| 17 | + var questioning = speech.endsWith("?"); |
| 18 | + var shouting = isShout.test(speech); |
| 19 | + if (questioning) { |
| 20 | + if (shouting) { |
| 21 | + return "Calm down, I know what I'm doing!"; |
| 22 | + } |
15 | 23 | return "Sure."; |
16 | | - |
| 24 | + } |
| 25 | + if (shouting) { |
| 26 | + return "Whoa, chill out!"; |
| 27 | + } |
17 | 28 | return "Whatever."; |
18 | 29 | } |
19 | | - |
20 | | - private boolean isShouting(String input) { |
21 | | - return input.chars() |
22 | | - .anyMatch(Character::isLetter) && |
23 | | - input.chars() |
24 | | - .filter(Character::isLetter) |
25 | | - .allMatch(Character::isUpperCase); |
26 | | - } |
27 | | - |
28 | | - private boolean isQuestioning(String input) { |
29 | | - return input.endsWith("?"); |
30 | | - } |
31 | | - |
32 | | - private boolean isSilent(String input) { |
33 | | - return input.length() == 0; |
34 | | - } |
35 | 30 | } |
36 | 31 | ``` |
37 | 32 |
|
38 | | -In this approach, the different conditions for Bob’s responses are separated into dedicated private methods within the `Bob` class. This method-based approach improves readability and modularity by organizing each condition check into its own method, making the main response method easier to understand and maintain. |
39 | | - |
40 | | -## Explanation |
41 | | - |
42 | | -This approach simplifies the main method `hey` by breaking down each response condition into helper methods: |
| 33 | +In this approach you have a series of `if` statements using the private methods to evaluate the conditions. |
| 34 | +As soon as the right condition is found, the correct response is returned. |
43 | 35 |
|
44 | | -### Trimming the Input |
| 36 | +Note that there are no `else if` or `else` statements. |
| 37 | +If an `if` statement can return, then an `else if` or `else` is not needed. |
| 38 | +Execution will either return or will continue to the next statement anyway. |
45 | 39 |
|
46 | | - The `input` is trimmed using the `String` [`trim()`][trim] method to remove any leading or trailing whitespace. This helps to accurately detect if the input is empty and should prompt a `"Fine. Be that way!"` response. |
| 40 | +The `String` [`trim()`][trim] method is applied to the input to eliminate any whitespace at either end of the input. |
| 41 | +If the string has no characters left, it returns the response for saying nothing. |
47 | 42 |
|
48 | | -### Delegating to Helper Methods |
| 43 | +~~~~exercism/caution |
| 44 | +Note that a `null` `string` would be different from a `String` of all whitespace. |
| 45 | +A `null` `String` would throw a `NullPointerException` if `trim()` were applied to it. |
| 46 | +~~~~ |
49 | 47 |
|
50 | | - Each condition is evaluated using the following helper methods: |
| 48 | +A [Pattern][pattern] is defined to look for at least one English alphabetic character. |
51 | 49 |
|
52 | | - 1. **`isSilent`**: Checks if the trimmed input has no characters. |
53 | | - 2. **`isShouting`**: Checks if the input is all uppercase and contains at least one alphabetic character, indicating shouting. |
54 | | - 3. **`isQuestioning`**: Verifies if the trimmed input ends with a question mark. |
| 50 | +The first half of the `isShout` [Predicate][predicate] |
55 | 51 |
|
56 | | - This modular approach keeps each condition encapsulated, enhancing code clarity. |
57 | | - |
58 | | -### Order of Checks |
59 | | - |
60 | | - The order of checks within `hey` is important: |
| 52 | +```java |
| 53 | +isAlpha.matcher(msg).find() && msg == msg.toUpperCase(); |
| 54 | +``` |
61 | 55 |
|
62 | | - 1. Silence is evaluated first, as it requires an immediate response. |
63 | | - 2. Shouted questions take precedence over individual checks for shouting and questioning. |
64 | | - 3. Shouting comes next, requiring its response if not combined with a question. |
65 | | - 4. Questioning (a non-shouted question) is checked afterward. |
| 56 | +is constructed from the `Pattern` [`matcher()`][matcher-method] method and the [`Matcher`][matcher] [`find()`][find] method |
| 57 | +to ensure there is at least one letter character in the `String`. |
| 58 | +This is because the second half of the condition tests that the uppercased input is the same as the input. |
| 59 | +If the input were only `"123"` it would equal itself uppercased, but without letters it would not be a shout. |
66 | 60 |
|
67 | | - This ordering ensures that Bob’s response matches the expected behavior without redundancy. |
| 61 | +A question is determined by use of the [`endsWith()`][endswith] method to see if the input ends with a question mark. |
68 | 62 |
|
69 | 63 | ## Shortening |
70 | 64 |
|
71 | | -When the body of an `if` statement is a single line, both the test expression and the body _could_ be put on the same line, like so: |
| 65 | +When the body of an `if` statement is a single line, both the test expression and the body _could_ be put on the same line, like so |
72 | 66 |
|
73 | 67 | ```java |
74 | | -if (isSilent(inputTrimmed)) return "Fine. Be that way!"; |
| 68 | +if (speech.isEmpty()) return "Fine. Be that way!"; |
75 | 69 | ``` |
76 | 70 |
|
77 | | -or the body _could_ be put on a separate line without curly braces: |
| 71 | +or the body _could_ be put on a separate line without curly braces |
78 | 72 |
|
79 | 73 | ```java |
80 | | -if (isSilent(inputTrimmed)) |
| 74 | +if (speech.isEmpty()) |
81 | 75 | return "Fine. Be that way!"; |
82 | 76 | ``` |
83 | 77 |
|
84 | | -However, the [Java Coding Conventions][coding-conventions] advise always using curly braces for `if` statements, which helps to avoid errors. Your team may choose to overrule them at its own risk. |
| 78 | +However, the [Java Coding Conventions][coding-conventions] advise to always use curly braces for `if` statements, which helps to avoid errors. |
| 79 | +Your team may choose to overrule them at its own risk. |
85 | 80 |
|
86 | | -[trim]: https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#trim() |
| 81 | +[trim]: https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#trim() |
| 82 | +[pattern]: https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html |
| 83 | +[predicate]: https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html |
| 84 | +[matcher]: https://docs.oracle.com/javase/8/docs/api/java/util/regex/Matcher.html |
| 85 | +[matcher-method]: https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html#matcher-java.lang.CharSequence- |
| 86 | +[find]: https://docs.oracle.com/javase/8/docs/api/java/util/regex/Matcher.html#find-- |
| 87 | +[endswith]: https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#endsWith(java.lang.String) |
87 | 88 | [coding-conventions]: https://www.oracle.com/java/technologies/javase/codeconventions-statements.html#449 |
0 commit comments