diff --git a/exercises/practice/bob/.approaches/config.json b/exercises/practice/bob/.approaches/config.json index d7a8ba83b..0b9e8afc5 100644 --- a/exercises/practice/bob/.approaches/config.json +++ b/exercises/practice/bob/.approaches/config.json @@ -2,9 +2,24 @@ "introduction": { "authors": [ "bobahop" + ], + "contributors": [ + "jagdish-15" ] }, "approaches": [ + { + "uuid": "6ca5c7c0-f8f1-49b2-b137-951fa39f89eb", + "slug": "method-based", + "title": "Method based", + "blurb": "Uses boolean functions to check conditions", + "authors": [ + "jagdish-15" + ], + "contributors": [ + "BenjaminGale" + ] + }, { "uuid": "323eb230-7f27-4301-88ea-19c39d3eb5b6", "slug": "if-statements", diff --git a/exercises/practice/bob/.approaches/introduction.md b/exercises/practice/bob/.approaches/introduction.md index f7b7389b2..8108ff29f 100644 --- a/exercises/practice/bob/.approaches/introduction.md +++ b/exercises/practice/bob/.approaches/introduction.md @@ -1,30 +1,58 @@ # Introduction -There are various idiomatic approaches to solve Bob. -A basic approach can use a series of `if` statements to test the conditions. -An array can contain answers from which the right response is selected by an index calculated from scores given to the conditions. +In this exercise, we’re working on a program to determine Bob’s responses based on the tone and style of given messages. Bob responds differently depending on whether a message is a question, a shout, both, or silence. Various approaches can be used to implement this logic efficiently and cleanly, ensuring the code remains readable and easy to maintain. -## General guidance +## General Guidance -Regardless of the approach used, some things you could look out for include +When implementing your solution, consider the following tips to keep your code optimized and idiomatic: -- If the input is trimmed, [`trim()`][trim] only once. +- **Trim the Input Once**: Use [`trim()`][trim] only once at the start to remove any unnecessary whitespace. +- **Use Built-in Methods**: For checking if a message is a question, prefer [`endsWith("?")`][endswith] instead of manually checking the last character. +- **Single Determinations**: Use variables for `questioning` and `shouting` rather than calling these checks multiple times to improve efficiency. +- **DRY Code**: Avoid duplicating code by combining the logic for determining a shout and a question when handling shouted questions. Following the [DRY][dry] principle helps maintain clear and maintainable code. +- **Return Statements**: An early return in an `if` statement eliminates the need for additional `else` blocks, making the code more readable. +- **Curly Braces**: While optional for single-line statements, some teams may require them for readability and consistency. -- Use the [`endsWith()`][endswith] `String` method instead of checking the last character by index for `?`. +## Approach: Method-Based -- Don't copy/paste the logic for determining a shout and for determining a question into determining a shouted question. - Combine the two determinations instead of copying them. - Not duplicating the code will keep the code [DRY][dry]. +```java +class Bob { + String hey(String input) { + var inputTrimmed = input.trim(); + + if (isSilent(inputTrimmed)) + return "Fine. Be that way!"; + if (isYelling(inputTrimmed) && isAsking(inputTrimmed)) + return "Calm down, I know what I'm doing!"; + if (isYelling(inputTrimmed)) + return "Whoa, chill out!"; + if (isAsking(inputTrimmed)) + return "Sure."; + + return "Whatever."; + } + + private boolean isYelling(String input) { + return input.chars() + .anyMatch(Character::isLetter) && + input.chars() + .filter(Character::isLetter) + .allMatch(Character::isUpperCase); + } -- Perhaps consider making `questioning` and `shouting` values set once instead of functions that are possibly called twice. + private boolean isAsking(String input) { + return input.endsWith("?"); + } -- If an `if` statement can return, then an `else if` or `else` is not needed. - Execution will either return or will continue to the next statement anyway. + private boolean isSilent(String input) { + return input.length() == 0; + } +} +``` -- If the body of an `if` statement is only one line, curly braces aren't needed. - Some teams may still require them in their style guidelines, though. +This approach defines helper methods for each type of message—silent, yelling, and asking—to keep each condition clean and easily testable. For more details, refer to the [Method-Based Approach][approach-method-based]. -## Approach: `if` statements +## Approach: `if` Statements ```java import java.util.function.Predicate; @@ -56,9 +84,9 @@ class Bob { } ``` -For more information, check the [`if` statements approach][approach-if]. +This approach utilizes nested `if` statements and a predicate for determining if a message is a shout. For more details, refer to the [`if` Statements Approach][approach-if]. -## Approach: answer array +## Approach: Answer Array ```java import java.util.function.Predicate; @@ -86,16 +114,21 @@ class Bob { } ``` -For more information, check the [Answer array approach][approach-answer-array]. +This approach uses an array of answers and calculates the appropriate index based on flags for shouting and questioning. For more details, refer to the [Answer Array Approach][approach-answer-array]. + +## Which Approach to Use? + +Choosing between the method-based approach, `if` statements, and answer array approach can come down to readability and maintainability. Each has its advantages: -## Which approach to use? +- **Method-Based**: Clear and modular, great for readability. +- **`if` Statements**: Compact and straightforward, suited for smaller projects. +- **Answer Array**: Minimizes condition checks by using indices, efficient for a variety of responses. -Since benchmarking with the [Java Microbenchmark Harness][jmh] is currently outside the scope of this document, -the choice between `if` statements and answers array can be made by perceived readability. +Experiment with these approaches to find the balance between readability and performance that best suits your needs. [trim]: https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#trim() [endswith]: https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#endsWith(java.lang.String) [dry]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself +[approach-method-based]: https://exercism.org/tracks/java/exercises/bob/approaches/method-based [approach-if]: https://exercism.org/tracks/java/exercises/bob/approaches/if-statements [approach-answer-array]: https://exercism.org/tracks/java/exercises/bob/approaches/answer-array -[jmh]: https://github.com/openjdk/jmh diff --git a/exercises/practice/bob/.approaches/method-based/content.md b/exercises/practice/bob/.approaches/method-based/content.md new file mode 100644 index 000000000..816b60643 --- /dev/null +++ b/exercises/practice/bob/.approaches/method-based/content.md @@ -0,0 +1,83 @@ +# Method-Based Approach + +```java +class Bob { + String hey(String input) { + var inputTrimmed = input.trim(); + + if (isSilent(inputTrimmed)) + return "Fine. Be that way!"; + if (isYelling(inputTrimmed) && isAsking(inputTrimmed)) + return "Calm down, I know what I'm doing!"; + if (isYelling(inputTrimmed)) + return "Whoa, chill out!"; + if (isAsking(inputTrimmed)) + return "Sure."; + + return "Whatever."; + } + + private boolean isYelling(String input) { + return input.chars() + .anyMatch(Character::isLetter) && + input.chars() + .filter(Character::isLetter) + .allMatch(Character::isUpperCase); + } + + private boolean isAsking(String input) { + return input.endsWith("?"); + } + + private boolean isSilent(String input) { + return input.length() == 0; + } +} +``` + +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. + +## Explanation + +This approach simplifies the main method `hey` by breaking down each response condition into helper methods: + +1. **Trimming the Input**: + 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. + +2. **Delegating to Helper Methods**: + Each condition is evaluated using the following helper methods: + + - **`isSilent`**: Checks if the trimmed input has no characters. + - **`isYelling`**: Checks if the input is all uppercase and contains at least one alphabetic character, indicating shouting. + - **`isAsking`**: Verifies if the trimmed input ends with a question mark. + + This modular approach keeps each condition encapsulated, enhancing code clarity. + +3. **Order of Checks**: + The order of checks within `hey` is important: + - Silence is evaluated first, as it requires an immediate response. + - Shouted questions take precedence over individual checks for yelling and asking. + - Yelling comes next, requiring its response if not combined with a question. + - Asking (a non-shouted question) is checked afterward. + + This ordering ensures that Bob’s response matches the expected behavior without redundancy. + +## Shortening + +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: + +```java +if (isSilent(inputTrimmed)) return "Fine. Be that way!"; +``` + +or the body _could_ be put on a separate line without curly braces: + +```java +if (isSilent(inputTrimmed)) + return "Fine. Be that way!"; +``` + +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. + +[trim]: https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#trim() +[coding-conventions]: https://www.oracle.com/java/technologies/javase/codeconventions-statements.html#449 diff --git a/exercises/practice/bob/.approaches/method-based/snippet.txt b/exercises/practice/bob/.approaches/method-based/snippet.txt new file mode 100644 index 000000000..80133d787 --- /dev/null +++ b/exercises/practice/bob/.approaches/method-based/snippet.txt @@ -0,0 +1,8 @@ +if (isSilent(inputTrimmed)) + return "Fine. Be that way!"; +if (isYelling(inputTrimmed) && isAsking(inputTrimmed)) + return "Calm down, I know what I'm doing!"; +if (isYelling(inputTrimmed)) + return "Whoa, chill out!"; +if (isAsking(inputTrimmed)) + return "Sure."; \ No newline at end of file