Don't give Boolean variables and parameters "super-universal" names like flag
or switchIt
. Instead, write what exactly they flag/switch.
For example, if a method's argument enables or disables fields with personal information then call it enablePersonalInfo
- you should describe what the argument is doing or which information it brings, you don't need to explain other programmers what Boolean type is! The only exception to this rule - if the meaning of the Boolean argument is conveyed by the method's name: for example, the method SetReadOnlyMode
can have an argument named simply switch
(so it's understood that passing false
cancels the mode).
Give Boolean variables and methods positive names. Avoid both - direct negation and words which carry a tinge of negativity.
Think positively! :-) That will simplify reading and understanding of Boolean expressions, especially if they are nested. As an example, you can see the expressions if !DocNotPrinted()
and if DocPrinted()
: they are logically equivalent, but the positive version is easier to understand. As you see, the difference appears even when the expressions are stand-alone, but imagine if they would be a part of a complex Boolean expression! You can contradict: and what if the calling code wants to ask if the document has not been printed yet? Whether if !DocPrinted()
is better than if DocNotPrinted()
? Ok, we always have a chance that our Boolean var or method will be used with NOT, but using of positive wording will cause maximum ONE negation in that situation, while using negative wording can result in TWO of them - "negation of negation"! The following table contains a few examples of "negative" and "positive" words:
NEGATIVE WORDS (TO AVOID): |
POSITIVE WORDS (TO USE INSTEAD): |
disabled |
enabled ) |
disallowed (prohibited ) |
allowed |
hidden |
displayed |
excluded |
included |
failed |
ok (succeeded ) |
But please remember - this advice is only about Boolean names. If your not-Boolean method performs a negative action, its name must reflect exactly that action, for example: DisableSaveButton
, ProhibitPrinting
, ExcludeExpiredRecords
.
Comparing values, try to use =
/==
instead of <>
/!=
.
PB | if ls_order_status <> n_order_status.CLOSED) then [actions for orders which are not closed] else [actions for closed orders] end if |
C# | if (orderStatus <> OrderStatuses.Closed) { [actions for orders which are not closed] } else { [actions for closed orders] } |
PB | if ls_order_status = n_order_status.CLOSED) then [actions for closed orders] else [actions for orders which are not closed] end if |
C# | if (orderStatus = OrderStatuses.Closed) { [actions for closed orders] } else { [actions for orders which are not closed] } |
This advice is especially important if the value comparison is nested into a Boolean expression:
PB | if ls_order_status <> n_order_status.CLOSED or & ls_state <> n_state.MONTANA or & ls_day_type <> n_day_type.WEEKEND then [actions if: order is not closed OR state differs from Montana OR day is not a weekend] else [actions for other cases] end if |
C# | if (orderStatus <> OrderStatuses.Closed || state <> States.Montana || dayType <> DayTypes.WeekEnd) { [actions if: order is not closed OR state differs from Montana OR day is not a weekend] } else { [actions for other cases] { |
PB | if ls_order_status = n_order_status.CLOSED and & ls_state = n_state.MONTANA and & ls_day_type = n_day_type.WEEKEND then [actions if: order is closed AND state is Montana AND day is a weekend] else [actions for other cases] end if |
C# | if (orderStatus = OrderStatuses.Closed && state = States.Montana && dayType = DayTypes.WeekEnd) { [actions if: order is closed AND state is Montana AND day is a weekend] } else { [actions for other cases] } |
Comparing one value with many other values, use the switch
/choose case
construction instead of multiple ||
/OR
operators, so the compared value will be mentioned in the code only once.
In this situation, we are not really switching/choosing between options (so we can call it "a fake switch/choose"), but why not to do our code more readable if the programming language doesn't allow the IN
clause like T-SQL and PL/SQL do?
PB | if ls_city = "Toronto" or ls_city = "Boston" or ls_city = "Chicago" then ls_message = "Nice city!" end if |
C# | if (city == "Toronto" || city == "Boston" || city == "Chicago") { message = "Nice city!"; } |
PB | choose case ls_city case "Toronto", "Boston", "Chicago" ls_message = "Nice city!" end choose |
C# | switch (city) { case "Toronto": case "Boston": case "Chicago": message = "Nice city!"; break; } |
PowerBuilder developers can also utilize the iin() function.
Don't write complicated expressions inside of if
statements (just after the if
); instead, store the expression's result in a Boolean variable with a quality, descriptive name and use that variable in the if
statement.
That will make the logic being implemented extremely easy to understand!
PB | if (ls_calc_method = n_calc_method.ADDITIVE and lb_additive_calculation_passed) or & (ls_calc_method = n_calc_method.RATIO and lb_ratio_calculation_passed) then... |
C# | if ((calcMethod == CalcMethods.Additive && additiveCalculationPassed) || (calcMethod == CalcMethods.Ratio && ratioCalculationPassed))... |
PB | lb_structural_change_occurred = & (ls_calc_method = n_calc_method.ADDITIVE and lb_additive_calculation_passed) or & (ls_calc_method = n_calc_method.RATIO and lb_ratio_calculation_passed) if lb_structural_change_occurred then... |
C# | bool structuralChangeOccurred; structuralChangeOccurred = (calcMethod == CalcMethods.Additive && additiveCalculationPassed) || (calcMethod == CalcMethods.Ratio && ratioCalculationPassed); if (structuralChangeOccurred)... |
As you see, the bad code tells us only WHEN the condition is true,
but the good code tells us both WHEN and WHY!!! It is so important
that I easily go against the rule to decrease scripts
size - I don't try to keep my scripts short in any price,
sometimes I prefer to ADD LINES if that makes the code more
understandable or less looking like a heap of rubbish. And, of
course, don't forget that the expression in my example is pretty
small because it's enough to convey the idea; in the real life I have
seen statements with half-a-screen Boolean expressions inside an if
! If the logic is complicated then don't populate the Boolean variable in one assignment statement (as described above) but come to the truth step by step, using a number of
simple Boolean expressions instead of one monstrous construction. Please see the next example of populating a variable step by step instead of inserting everything in the if
statement:
PB | boolean lb_the_show_must_go_on lb_the_show_must_go_on = false choose case true case [cond1], [cond2] lb_the_show_must_go_on = true case [cond3] and [cond4] lb_the_show_must_go_on = this.uf_xxx() case [cond3] lb_the_show_must_go_on = this.uf_yyy() end choose if lb_the_show_must_go_on then... |
C# | bool theShowMustGoOn; theShowMustGoOn = false; if ([cond1]||[cond2]) { theShowMustGoOn = true; } elseif ([cond3]&&[cond4]) { theShowMustGoOn = this.isXxx(); } elseif ([cond3]) { theShowMustGoOn = this.isYyy(); } if (theShowMustGoOn)... |
If a method returns Boolean then its name should convey the meaning of the returned value to make the business logic in the calling scripts easily understandable. Calling that function as an if
statement's condition must produce a correct real-English sentence.
Here are some examples of well-named Boolean methods: RowIsValidated
, OrdersAreOk
, FileNameAlreadyExists
. A strange tradition dictates to put the words "Is"/"Are"/"Does"/"Do" in the beginning (like IsRowValidated
, AreOrdersOk
, DoesFileNameAlreadyExist
etc.), but I personally don't like that approach because it produces incorrect English in calling scripts: we say "if row is validated then do something", not "if is row validated...". Of course, it's not an issue of high importance - you can even omit "Is"/"Are" at all, and the names still will be fine: RowValidated
, OrdersOk
.
Never use verbs in imperative to name Boolean methods. For example, the following names are absolutely unacceptable for methods returning Boolean: ValidateRow
, CheckOrders
, CheckFileExistence
- we don't say "if validate row then save", "if check orders then print", "if check file existence then open file"!
If it's possible, populate Boolean variables with results of Boolean expressions instead of direct assigning of true
and false
.
PB | if ll_row_count > 1 then lb_multi_rows_mode = true else lb_multi_rows_mode = false end if |
C# | if (rowCount > 1) { multiRowsMode = true; } else { multiRowsMode = false; } |
PB | lb_multi_rows_mode = (ll_row_count > 1) |
C# | multiRowsMode = (rowCount > 1); |
Boolean functions must always return true
or false
(but not null). Think defensively if the function fails to return the asked value.
You cannot be sure if the calling script (which must make a code branching decision) will check the returned result for null. In fact, usually there is no such check, especially when the function is directly put inside an if
construction. So, there is a high probability that a null, returned by a Boolean method, will be treated as false
- it's acceptable in most cases, but not always. So, what do we have to return if the method cannot answer the asked question? In this situation, think pessimistically. If the method checks an eligibility, return a value, saying "the entity is not eligible for what you asked". For example, if the method CustomerCanGetDiscount()
failed to connect to the database, it should return false
, but the method PassengerMustBeDoubleCheckedBySecurity
should return true
in that situation - simply use the common sense trying to prevent the bad. But the best way to treat technical failures is throwing an exception!
Simplify Boolean expressions using De Morgan's Laws.
To ensure that logical statements are expressed simply, use the
(NOT P) OR (NOT Q) can be expressed shorter as NOT (P AND Q)
(NOT P) AND (NOT Q) can be expressed shorter as NOT (P OR Q)
So, in that situation, use the NOT operator only once, as shown below.
PB | lb_is_gas = (not lb_is_liquid) and (not lb_is_solid) and (not lb_is_plasma) |
C# | isGas = (!isLiquid) && (!isSolid) && (!isPlasma); |
PB | lb_is_gas = not (lb_is_liquid or lb_is_solid) |
C# | isGas = !(isLiquid || isSolid || isPlasma); |
PB | lb_we_will_stay_home = (not lb_we_are_hungry) or (not lb_restaurants_are_open_now) |
C# | weWillStayHome = (!weAreHungry) || (!restaurantsAreOpenNow); |
PB | lb_we_will_stay_home = not (lb_we_are_hungry and lb_restaurants_are_open_now) |
C# | weWillStayHome = !(weAreHungry && restaurantsAreOpenNow); |
Use short-circuit evaluation to improve performance of your program.
Short-circuit evaluation (SCE), also called "minimal", denotes the semantics of Boolean operators in logical expressions in formats "A AND B" and "A OR B" in which the second argument is only executed or evaluated if the first argument does not suffice to determine the value of the expression. In other words, the evaluation of a Boolean expression terminates as soon as the result value, produced by the whole expression, is known, even if not all the components in the expression have been evaluated. Thus, try to build the expression so that the decision is made as soon as possible:
false
, the whole expression will return false
(B will not be evaluated), so make sure that A is more efficient (executed quicker) than B (and if they have a same performance, make sure that A is more likely to produce false
than B).
true
, the whole expression will return true
(B will not be evaluated), so make sure that A is more efficient (executed quicker) than B (and if they have a same performance, make sure that A is more likely to produce true
than B).
To clarify the idea, let's write a Boolean method which determines if the person is eligible to work in security (to pass the validation, the person must be older than the minimum age, defined by law, AND to have no criminal record). The age checking is simply a quick comparison between instance variables, but to check criminal record, the application needs to send a query to the database via the network (which is much slower operation). Here is the method's code with the correct order of components (PB doesn't have short-circuit evaluation, so we have to imitate it by slightly more complicated code):
PB | if this.ii_age < this.ii_min_age then return false
|
C# | return (this.Age >= this.MinAge && this.HasClearCriminalRecord()); |
So, if the candidate is immature, we will not bother the network and the DB at all.
The short-circuit is not the only possible evaluation, there is also the regular one (also called "eager" or "long-circuit") - when the second part of the Boolean expression is evaluated anyway, even if its result cannot change the already known value, which will be produced by the whole expression. In most programming languages you can utilize both the evaluations using different logical operators, but some languages have only one. Look at the following table to ascertain what happens in the language you are working with:
Language | Eager operators | Short-circuit operators |
---|---|---|
Ada, Eiffel | and , or |
and then , or else |
C, C++ | none | && , || |
C# | & , | |
&& , || |
ColdFusion | none | AND , OR , && , || |
Fortran | .and. , .or. |
none |
Java | & , | |
&& , || |
JavaScript | none | && , || |
Lisp | none | and , or |
Pascal | and , or |
and_then , or_else |
Perl | & , | |
&& , and , || , or |
PHP | none | && , and , || , or |
PL/SQL | none | AND , OR |
PowerScript (PowerBuilder) | and , or |
none |
Python | none | and , or |
Smalltalk | & , | |
and: , or: |
Transact-SQL (T-SQL) | none | AND , OR |
VB.NET | And , Or |
AndAlso , OrElse |
VB, VBA, VB Script | And , Or |
Select Case |
If your programming language doesn't appear in the table, you can easily check if it supports SCE by assignin a Boolean variable the results of the expressions, having zero divide in the second part (translate them to your language if needed):
BooleanVariable = ((1 = 2) AND (1 = (1 / 0)))
BooleanVariable = ((1 = 1) OR (1 = (1 / 0)))
If you don't get the zero divide error then SCE is supported.
<< prev | CONTENTS | next >> |