Maybe late answer but I'd go to a completely different direction.
I think isDestinationValid
variable (for example) doesn't materialize magically. There is an expression to calculate it and it's inside the function you use to determine which message you should return to your users: you're mixing logic and presentation in the same function. From comments I see you have all these logic inside your doPost()
method. In my opinion it's too much, to understand what happen when you post data you have to go through code that:
- Fetches data from database.
- Validates your inputs both by their own and using DB data.
- Prepares output messages.
- Performs required task.
Waaaay to many things to do in one single method. When you have a complex logic condition then it probably deserves its own name (because it may be reused or because details may change in future). First of all I'd refactor out local variables introducing separate functions:
boolean canBookTrip() {
return isExistingDestination() && isAccountInformationValid();
}
boolean isAccountInformationValid() {
return isBillingNameFilled() && isValidAmount();
}
And so on. Of course take this with cum grano salis. When an expression is trivial and you won't reuse it then you don't need to have a function for that. For example isBillingNameFilled()
may be a local variable however isValidAmount()
and isExistingDestination()
deal with database and I strongly suggest to make a separate function for them.
Few more notes:
isExistingDestination()
should both check for a valid (non-empty) and existing destination. It's not astonishing if an empty destination does not exist.
- It's absolutely not mandatory but usually not-negated logical conditions are easier to read. I'd make them all positive.
- Compiler will do optimizations for you then I'd split such long condition into multiple functions/local variables.
Your final code will then be:
String getTripBookingMessage() {
if (canBookTrip())
return "Have a safe trip!";
return "Please try again!";
}
Note that your code stays in its own separate function. canBookTrip()
will be probably reused somewhere else (book confirmation after submitting?) but you won't repeat same code again and again. Also, any change (for example, if you will allow a free pre-booking) won't affect all your code, but just this one very specific function.
EDIT for your own answer: you introduced InputUtils
class. Utility/helper classes are more often than not a big code smell. Is there any reason they can't be private methods inside calling class? They will also should have access to HttpServletResponse
. More than that you may want to have a Booking
class responsible for booking (note that somewhere you should also manage concurrency) with booking information from Trip
(using a Destination
class). Also note that if (let's say) amountIsValid
is false
then you don't need to calculate amount
with a dummy value. Also - let me repeat - I wouldn't do everything inside doPost()
method. It's really no-no.
Note from comments: "...more often than not..." obviously does not mean always. Sometimes they're a viable and even good solution. However sometimes doesn't allow you to use them always. Not only because of academic OOP style reason but because helper methods are usually static and static methods are a nightmare to test. It doesn't matter if you adhere to TDD/BDD or not, for sure you need tests.
Ideally it should be something like this:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
if (inputsAreValid(request))
bookTrip(request, response);
else
rejectRequest(request, response);
}
This is important just for one reason: if you have to change something about booking then you directly go to bookTrip()
and you can understand where to go in two seconds, looking doPost()
. Moreover you're reasonably sure that anything you will do in bookTrip()
won't affect rejectRequest()
. If you have all your code in one function then you have to re-read everything every time you change something. For example, in your code, will changing destination
also affects workflow when request is rejected?
System.out.println()
or embedded in a JSON HTTP response?) we could actually provide more useful advice than just shuffling your tokens around. \$\endgroup\$Trip
class or whatever. \$\endgroup\$