CSC/ECE 517 Fall 2009/wiki3 9 rp: Difference between revisions
No edit summary |
No edit summary |
||
Line 1: | Line 1: | ||
= The Non-Redundancy Principle = | = The Non-Redundancy Principle = | ||
In Bertrand Meyer's ''Object Oriented Software Construction'', he | In Bertrand Meyer's ''Object Oriented Software Construction'', he outlines many principles to increase reusability by managing complexity in software design. Some of these apply primarily to object oriented programming, primarily the principles dedicated to abstract data types. Most of the principles, however, apply to programming generally. The first five discussed in the book are often discussed in articles (the "Open-Close Principle" perhaps being the most popular), but equally important principles are discussed later in the book as well. One such principle, presented in the section on ''Contracting For Software Reliability'', is called The Non-Redundancy Principle. | ||
outlines many principles to increase reusability by managing | |||
complexity in software design. Some of these apply primarily to | |||
object oriented programming, primarily the principles dedicated to | |||
abstract data types. Most of the principles, however, apply to | |||
programming generally. The first five discussed in the book are often | |||
discussed in articles (the "Open-Close Principle" perhaps being the | |||
most popular), but equally important principles are discussed later in | |||
the book as well. One such principle, presented in the section on | |||
''Contracting For Software Reliability'', is called The Non-Redundancy | |||
Principle. | |||
== Definition == | == Definition == | ||
As with all principles presented in ''Object Oriented Software | As with all principles presented in ''Object Oriented Software Construction'', the principle itself is quite terse: | ||
Construction'', the principle itself is quite terse: | |||
<blockquote> Under no circumstances shall the body of a routine ever | <blockquote> Under no circumstances shall the body of a routine ever test for the routine's precondition. </blockquote> | ||
test for the routine's precondition. </blockquote> | |||
As simple as this may seem on first blush, it has extensive | As simple as this may seem on first blush, it has extensive implications. | ||
implications. | |||
Before we discuss the implications, it is proper to provide a bit of | Before we discuss the implications, it is proper to provide a bit of context regarding preconditions, postconditions and programming by contract. | ||
context regarding preconditions, postconditions and programming by | |||
contract. | |||
== Context == | == Context == | ||
Meyer outlined the notion of ''programming by contract'', which boils | Meyer outlined the notion of ''programming by contract'', which boils down to a methodology to enforce software modularity by clearly defining the relationships between various components, much in the same way relationships between individuals and/or companies are formalized in contracts. | ||
down to a methodology to enforce software modularity by clearly | |||
defining the relationships between various components, much in the | |||
same way relationships between individuals and/or companies are | |||
formalized in contracts. | |||
In Chapter 11, Meyer indicates that for each party in the contract, | In Chapter 11, Meyer indicates that for each party in the contract, the aspects of the contract include: | ||
the aspects of the contract include: | |||
* Rights | * Rights | ||
Line 44: | Line 24: | ||
* Postconditions | * Postconditions | ||
In short, these indicate what is expected of the ''supplier'' and the | In short, these indicate what is expected of the ''supplier'' and the ''client'' (terms describing the roles of objects that are party to the contract). Specifically, a ''precondition'' is a minimum criteria that must be satisfied on the part of the client for the contract to be valid. The heart of the Non-Redundancy Principle is ''who'' checks that the preconditions are satisfied, and ''when'' are those checks made. | ||
''client'' (terms describing the roles of objects that are party to | |||
the contract). Specifically, a ''precondition'' is a minimum criteria | |||
that must be satisfied on the part of the client for the contract to | |||
be valid. The heart of the Non-Redundancy Principle is ''who'' checks | |||
that the preconditions are satisfied, and ''when'' are those checks | |||
made. | |||
It must be decided whether the client or the supplier of a service | It must be decided whether the client or the supplier of a service should enforce the preconditions. Once that is made clear, the software designer must decide when the precondition will be enforced, and once it is enforced, it should never be checked. | ||
should enforce the preconditions. Once that is made clear, the | |||
software designer must decide when the precondition will be enforced, | |||
and once it is enforced, it should never be checked. | |||
Meyer goes to some length to justify this position, which flies in the | Meyer goes to some length to justify this position, which flies in the face of the defensive programming mantra, which generally encourages as much checking as possible, ostensibly to help eliminate errors "just in case". As we will see in the following sections, and as Meyer describes, it is difficult to justify this approach, purly on the grounds that such redundant checking inordinately increases the complexity of the software. | ||
face of the defensive programming mantra, which generally encourages | |||
as much checking as possible, ostensibly to help eliminate errors | |||
"just in case". As we will see in the following sections, and as | |||
Meyer describes, it is difficult to justify this approach, purly on | |||
the grounds that such redundant checking inordinately increases the | |||
complexity of the software. | |||
== Implications == | == Implications == | ||
There are a variety of implications that emerge from the | There are a variety of implications that emerge from the Non-Redundancy Principle. | ||
Non-Redundancy Principle. | |||
=== DRY Principle === | === DRY Principle === | ||
A common mantra in programming is "Don't Repeat Yourself" or simply | A common mantra in programming is "Don't Repeat Yourself" or simply "DRY" for short. In it's simplest form, the Non-Redundancy Principle is a specialization of DRY, in that it prohibits developers from repeating themselves. | ||
"DRY" for short. In it's simplest form, the Non-Redundancy Principle | |||
is a specialization of DRY, in that it prohibits developers from | |||
repeating themselves. | |||
Consider the example that Meyer uses of a function responsible for | Consider the example that Meyer uses of a function responsible for returning a real positive square root of a number. In Java, such a function might look like: | ||
returning a real positive square root of a number. In Java, such a function | |||
might look like: | |||
/** | /** | ||
Line 88: | Line 47: | ||
} | } | ||
What happens if the number passed in is negative? Will the method | What happens if the number passed in is negative? Will the method return a real square root? We have specified in the supplier (in this case, the method called "sqrt") is obligated to return a positive real square root. By specifying a precondition that the input must be greater than or equal to zero, we now also qualify that obligation by imposing it only if the precondition is satisfied. | ||
return a real square root? We have specified in the supplier (in this | |||
case, the method called "sqrt") is obligated to return a positive real | |||
square root. By specifying a precondition that the input must be | |||
greater than or equal to zero, we now also qualify that obligation by | |||
imposing it only if the precondition is satisfied. | |||
/** | /** | ||
Line 103: | Line 57: | ||
} | } | ||
Now we have a precondition. But what does all this have to do with | Now we have a precondition. But what does all this have to do with the Non-Redundancy Principle? | ||
the Non-Redundancy Principle? | |||
It might be tempting to then write this: | It might be tempting to then write this: | ||
Line 117: | Line 70: | ||
} | } | ||
But, according to the Non-Redundancy Principle, this is prohibited! | But, according to the Non-Redundancy Principle, this is prohibited! Why? Well, what does Math.sqrt do? Does it also check for a value less than zero? It turns out that it does. But the point here is that if we use the code directly above this paragraph, our system now checkes for numbers less than zero ''twice''. Not only have we repeated ourselves, but we have increased the complexity of the code, which brings us to how the Non-Redundancy Principle reduces code complexity. | ||
Why? Well, what does Math.sqrt do? Does it also check for a value | |||
less than zero? It turns out that it does. But the point here is | |||
that if we use the code directly above this paragraph, our system now | |||
checkes for numbers less than zero ''twice''. Not only have we | |||
repeated ourselves, but we have increased the complexity of the code, | |||
which brings us to how the Non-Redundancy Principle reduces code | |||
complexity. | |||
=== Code Complexity === | === Code Complexity === | ||
One way to measure code complexity is through [http://www.mccabe.com/iq_research_iqgloss.htm#cyclomatic cyclomatic complexity] (sometimes called the ''McCabe Number''). Simply put, cyclomatic complexity measures the number of different paths through a piece of code. It is often cited as a good measure of how maintainable a piece of code is, and certainly has an impact on how burdensome testing will be. In the example above: | |||
public double sqrt(double d) { | |||
if (d < 0) throw new IllegalArgumentException("Input must be greater than or equal to zero."); | |||
return Math.sqrt(d); | |||
} | |||
We can immediately see that adding the check for the precondition to the method added an additional exit from the method, namely, the method can now throw an IllegalArgumentExecetion. Taken singly, this may not seem like a big deal, but imagine methods with two or more preconditions, and imagine every method in the system starts checking for each. On a systemic scale, redundant checking vastly increases the code bulk, complexity, and testing burden of the system. | |||
=== Run-time versus Compile-time Errors === | === Run-time versus Compile-time Errors === |
Revision as of 07:38, 18 November 2009
The Non-Redundancy Principle
In Bertrand Meyer's Object Oriented Software Construction, he outlines many principles to increase reusability by managing complexity in software design. Some of these apply primarily to object oriented programming, primarily the principles dedicated to abstract data types. Most of the principles, however, apply to programming generally. The first five discussed in the book are often discussed in articles (the "Open-Close Principle" perhaps being the most popular), but equally important principles are discussed later in the book as well. One such principle, presented in the section on Contracting For Software Reliability, is called The Non-Redundancy Principle.
Definition
As with all principles presented in Object Oriented Software Construction, the principle itself is quite terse:
Under no circumstances shall the body of a routine ever test for the routine's precondition.
As simple as this may seem on first blush, it has extensive implications.
Before we discuss the implications, it is proper to provide a bit of context regarding preconditions, postconditions and programming by contract.
Context
Meyer outlined the notion of programming by contract, which boils down to a methodology to enforce software modularity by clearly defining the relationships between various components, much in the same way relationships between individuals and/or companies are formalized in contracts.
In Chapter 11, Meyer indicates that for each party in the contract, the aspects of the contract include:
- Rights
- Obligations
- Preconditions
- Postconditions
In short, these indicate what is expected of the supplier and the client (terms describing the roles of objects that are party to the contract). Specifically, a precondition is a minimum criteria that must be satisfied on the part of the client for the contract to be valid. The heart of the Non-Redundancy Principle is who checks that the preconditions are satisfied, and when are those checks made.
It must be decided whether the client or the supplier of a service should enforce the preconditions. Once that is made clear, the software designer must decide when the precondition will be enforced, and once it is enforced, it should never be checked.
Meyer goes to some length to justify this position, which flies in the face of the defensive programming mantra, which generally encourages as much checking as possible, ostensibly to help eliminate errors "just in case". As we will see in the following sections, and as Meyer describes, it is difficult to justify this approach, purly on the grounds that such redundant checking inordinately increases the complexity of the software.
Implications
There are a variety of implications that emerge from the Non-Redundancy Principle.
DRY Principle
A common mantra in programming is "Don't Repeat Yourself" or simply "DRY" for short. In it's simplest form, the Non-Redundancy Principle is a specialization of DRY, in that it prohibits developers from repeating themselves.
Consider the example that Meyer uses of a function responsible for returning a real positive square root of a number. In Java, such a function might look like:
/** * Returns the postive real square root of the input, d. */ public double sqrt(double d) { return Math.sqrt(d); }
What happens if the number passed in is negative? Will the method return a real square root? We have specified in the supplier (in this case, the method called "sqrt") is obligated to return a positive real square root. By specifying a precondition that the input must be greater than or equal to zero, we now also qualify that obligation by imposing it only if the precondition is satisfied.
/** * Returns the postive real square root of the input, d. * Precondition: input d >= 0 */ public double sqrt(double d) { return Math.sqrt(d); }
Now we have a precondition. But what does all this have to do with the Non-Redundancy Principle?
It might be tempting to then write this:
/** * Returns the postive real square root of the input, d. * Precondition: input d >= 0 */ public double sqrt(double d) { if (d < 0) throw new IllegalArgumentException("Input must be greater than or equal to zero."); return Math.sqrt(d); }
But, according to the Non-Redundancy Principle, this is prohibited! Why? Well, what does Math.sqrt do? Does it also check for a value less than zero? It turns out that it does. But the point here is that if we use the code directly above this paragraph, our system now checkes for numbers less than zero twice. Not only have we repeated ourselves, but we have increased the complexity of the code, which brings us to how the Non-Redundancy Principle reduces code complexity.
Code Complexity
One way to measure code complexity is through cyclomatic complexity (sometimes called the McCabe Number). Simply put, cyclomatic complexity measures the number of different paths through a piece of code. It is often cited as a good measure of how maintainable a piece of code is, and certainly has an impact on how burdensome testing will be. In the example above:
public double sqrt(double d) { if (d < 0) throw new IllegalArgumentException("Input must be greater than or equal to zero."); return Math.sqrt(d); }
We can immediately see that adding the check for the precondition to the method added an additional exit from the method, namely, the method can now throw an IllegalArgumentExecetion. Taken singly, this may not seem like a big deal, but imagine methods with two or more preconditions, and imagine every method in the system starts checking for each. On a systemic scale, redundant checking vastly increases the code bulk, complexity, and testing burden of the system.