This pattern generalises to any method where you are currently returning a boolean…. but it is “true” for many different reasons and “false” if and only if none of the other reasons apply.
As a concrete example consider this commonish task….
Decide whether or not you must rebuild something depending on whether the output exists and / or whether the output is older than any of the inputs it depends on.
Pretty much what “make” does for you.
But suppose you are writing a method “must_build?”, the “obvious” return type is boolean true / false.
I have settled on a better pattern….
I always return a frozen const string.
Why? Because it enables me to efficiently inform my user why I made the choice I did.
For example, (speaking ruby, but the pattern translates to other languages)
BUILD_REASON_OUTPUT_DOESNT_EXIST = 'Building because output doesnt exist".freeze BUILD_REASON_OLDER_MTIME = 'Rebuilding because output had older mtime than input".freeze BUILD_REASON_DONT_BUILD = 'Not rebuilding because file is up to date'.freeze def must_build? return BUILD_REASON_OUTPUT_DOESNT_EXIST unless FileTest.exists? output_file return BUILD_REASON_OLDER_MTIME if File.stat(output_file).mtime < File.stat(input_file).mtime BUILD_REASON_DONT_BUILD end
build_reason = must_build? # Use object identity equal?() if !build_reason.equal?( BUILD_REASON_DONT_BUILD) build end # Log what you are doing and why.... log build_reason