|
1 | 1 | # `if` statements |
2 | 2 |
|
3 | 3 | ```java |
4 | | -import java.util.function.Predicate; |
5 | | -import java.util.regex.Pattern; |
6 | | - |
7 | 4 | class Bob { |
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 | | - } |
23 | | - return "Sure."; |
24 | | - } |
25 | | - if (shouting) { |
| 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)) |
26 | 13 | return "Whoa, chill out!"; |
27 | | - } |
| 14 | + if (isQuestioning(inputTrimmed)) |
| 15 | + return "Sure."; |
| 16 | + |
28 | 17 | return "Whatever."; |
29 | 18 | } |
| 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 | + } |
30 | 35 | } |
31 | 36 | ``` |
32 | 37 |
|
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. |
| 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. |
35 | 39 |
|
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. |
| 40 | +## Explanation |
39 | 41 |
|
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. |
| 42 | +This approach simplifies the main method `hey` by breaking down each response condition into helper methods: |
42 | 43 |
|
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 | | -~~~~ |
| 44 | +### Trimming the Input |
47 | 45 |
|
48 | | -A [Pattern][pattern] is defined to look for at least one English alphabetic character. |
| 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. |
49 | 47 |
|
50 | | -The first half of the `isShout` [Predicate][predicate] |
| 48 | +### **Delegating to Helper Methods**: |
| 49 | + |
| 50 | + Each condition is evaluated using the following helper methods: |
51 | 51 |
|
52 | | -```java |
53 | | -isAlpha.matcher(msg).find() && msg == msg.toUpperCase(); |
54 | | -``` |
| 52 | + - **`isSilent`**: Checks if the trimmed input has no characters. |
| 53 | + - **`isShouting`**: Checks if the input is all uppercase and contains at least one alphabetic character, indicating shouting. |
| 54 | + - **`isQuestioning`**: Verifies if the trimmed input ends with a question mark. |
| 55 | + |
| 56 | + This modular approach keeps each condition encapsulated, enhancing code clarity. |
55 | 57 |
|
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. |
| 58 | +### **Order of Checks**: |
| 59 | + |
| 60 | + The order of checks within `hey` is important: |
| 61 | + - Silence is evaluated first, as it requires an immediate response. |
| 62 | + - Shouted questions take precedence over individual checks for yelling and asking. |
| 63 | + - Yelling comes next, requiring its response if not combined with a question. |
| 64 | + - Asking (a non-shouted question) is checked afterward. |
60 | 65 |
|
61 | | -A question is determined by use of the [`endsWith()`][endswith] method to see if the input ends with a question mark. |
| 66 | + This ordering ensures that Bob’s response matches the expected behavior without redundancy. |
62 | 67 |
|
63 | 68 | ## Shortening |
64 | 69 |
|
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 |
| 70 | +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: |
66 | 71 |
|
67 | 72 | ```java |
68 | | -if (speech.isEmpty()) return "Fine. Be that way!"; |
| 73 | +if (isSilent(inputTrimmed)) return "Fine. Be that way!"; |
69 | 74 | ``` |
70 | 75 |
|
71 | | -or the body _could_ be put on a separate line without curly braces |
| 76 | +or the body _could_ be put on a separate line without curly braces: |
72 | 77 |
|
73 | 78 | ```java |
74 | | -if (speech.isEmpty()) |
| 79 | +if (isSilent(inputTrimmed)) |
75 | 80 | return "Fine. Be that way!"; |
76 | 81 | ``` |
77 | 82 |
|
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. |
| 83 | +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. |
80 | 84 |
|
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) |
| 85 | +[trim]: https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#trim() |
88 | 86 | [coding-conventions]: https://www.oracle.com/java/technologies/javase/codeconventions-statements.html#449 |
0 commit comments