CSC/ECE 517 Fall 2009/wiki3 9 lkf: Difference between revisions
Line 100: | Line 100: | ||
Preconditions may be of two types: demanding and tolerant. Demanding preconditions place the burden on the client routine to ensure that the precondition for correct execution of the subroutine is met, i.e. the validation code is in the calling routine. Tolerant preconditions place the burden on the subroutine to ensure that the precondition for correct execution of the subroutine is met, i.e. the validation code is inside the subroutine [10]. Adherence to the non-redundancy principle stipulates that the same condition should only be tested by either demanding or tolerant preconditions but not by both. | Preconditions may be of two types: demanding and tolerant. Demanding preconditions place the burden on the client routine to ensure that the precondition for correct execution of the subroutine is met, i.e. the validation code is in the calling routine. Tolerant preconditions place the burden on the subroutine to ensure that the precondition for correct execution of the subroutine is met, i.e. the validation code is inside the subroutine [10]. Adherence to the non-redundancy principle stipulates that the same condition should only be tested by either demanding or tolerant preconditions but not by both. | ||
The following code fragment illustrates the use of a demanding precondition: | |||
<pre> | |||
// This method returns a given string after removing trailing and leading spaces | |||
private static string removeWhiteSpace(string input) | |||
{ | |||
string output; | |||
output = input.TrimStart(); | |||
output = output.TrimEnd(); | |||
return output; | |||
} | |||
static void Main(string[] args) | |||
{ | |||
string input; | |||
string output; | |||
input = " Have a nice day! "; | |||
Console.WriteLine("The input is: {0}", input); | |||
if (!string.IsNullOrEmpty(input)) | |||
{ | |||
output = removeWhiteSpace(input); | |||
Console.WriteLine("The output without whitespace is: {0}", output); | |||
} | |||
else | |||
{ | |||
Console.WriteLine("A valid string must be provided."); | |||
} | |||
Console.ReadKey(); | |||
} | |||
</pre> | |||
The following code fragment illustrates the use of a tolerant precondition: | |||
<pre> | |||
// This method returns a given string after removing trailing and leading spaces | |||
private static string removeWhiteSpace(string input) | |||
{ | |||
string output; | |||
if (!string.IsNullOrEmpty(input)) | |||
{ | |||
output = input.TrimStart(); | |||
output = output.TrimEnd(); | |||
} | |||
else | |||
{ | |||
return string.Empty; | |||
} | |||
return output; | |||
} | |||
static void Main(string[] args) | |||
{ | |||
string input; | |||
string output; | |||
input = " Have a nice day! "; | |||
Console.WriteLine("The input is: {0}", input); | |||
output = removeWhiteSpace(input); | |||
Console.WriteLine("The output without whitespace is: {0}", output); | |||
Console.ReadKey(); | |||
} | |||
'''Note how the subroutine code handles the presence of inadequate input, making it more forgiving or tolerant''' | |||
</pre> | |||
The use of tolerant preconditions is more in accordance with the application of defensive programming techniques. The use of demanding preconditions is in agreement with the non-redundancy principle. | |||
== Applications of the Non-Redundancy Principle == | == Applications of the Non-Redundancy Principle == |
Revision as of 19:03, 18 November 2009
This is currently work in progress, please do not review yet
Non-Redundancy Principle
Topic: Non-Redundancy Principle
Topic Description: Programming by contract, and design by contract, are well covered in Wikipedia and across the Web. But the Non-Redundancy Principle, an important part of programming by contract, is not. Explore the applications and implications of the principle that says that the body of a routine should never test for the routine's precondition.
Introduction
The Non-Redundancy principle indicates that subroutines must never test for the validity of that which is stated as a precondition for the subroutine's execution. A precondition is a property that must be true for a subroutine to execute successfully [3]. The subroutine itself should not include code to verify that the preconditions for its execution are met by the given input. According to the non-redundancy principle, checking for preconditions is the responsibility of the client code. The most widely used definition of the principle is given by Meyer, who states, "under no circumstances shall the body of a routine ever test for its precondition[3]."
For example, a routine that calculates the square root of a number would violate the Non-Redundancy principle if its code tests to make sure that the input given is not a negative number, as shown in the following code fragment [8].
// This method returns the square root of a floating point number private static double squareRoot(double input) { if (input >= 0) { return Math.Sqrt(input); } else { throw new Exception("The number provided does not have a square root."); } } static void Main(string[] args) { double input; double root; input = -4; if (input >= 0) { root = squareRoot(input); Console.WriteLine("The square root of {0} is {1}", input, root); } else { Console.WriteLine("The number provided does not have a square root."); } Console.ReadKey(); }
According to the Non-Redundancy principle, testing for the same precondition should not occur more than once in the code-base, hence eliminating redundant code. The Non-Redundancy principle is part of the [Design by Contract] methodology, which proposes that the relationship between a class and its clients must be defined in terms of a formal agreement that clearly states the responsibilities and duties of each party [5]. The agreement may be enforced or guaranteed with the use of preconditions.
Proper application of the non-redundancy principle to the code shown above results in the code shown in the next example, notice that the subroutine no longer checks the validity of the input:
// This method returns the square root of a floating point number private static double squareRoot(double input) { return Math.Sqrt(input); } static void Main(string[] args) { double input; double root; input = -4; if (input >= 0) { root = squareRoot(input); Console.WriteLine("The square root of {0} is {1}", input, root); } else { Console.WriteLine("The number provided does not have a square root."); } Console.ReadKey(); }
Preconditions may be of two types: demanding and tolerant. Demanding preconditions place the burden on the client routine to ensure that the precondition for correct execution of the subroutine is met, i.e. the validation code is in the calling routine. Tolerant preconditions place the burden on the subroutine to ensure that the precondition for correct execution of the subroutine is met, i.e. the validation code is inside the subroutine [10]. Adherence to the non-redundancy principle stipulates that the same condition should only be tested by either demanding or tolerant preconditions but not by both.
The following code fragment illustrates the use of a demanding precondition:
// This method returns a given string after removing trailing and leading spaces private static string removeWhiteSpace(string input) { string output; output = input.TrimStart(); output = output.TrimEnd(); return output; } static void Main(string[] args) { string input; string output; input = " Have a nice day! "; Console.WriteLine("The input is: {0}", input); if (!string.IsNullOrEmpty(input)) { output = removeWhiteSpace(input); Console.WriteLine("The output without whitespace is: {0}", output); } else { Console.WriteLine("A valid string must be provided."); } Console.ReadKey(); }
The following code fragment illustrates the use of a tolerant precondition:
// This method returns a given string after removing trailing and leading spaces private static string removeWhiteSpace(string input) { string output; if (!string.IsNullOrEmpty(input)) { output = input.TrimStart(); output = output.TrimEnd(); } else { return string.Empty; } return output; } static void Main(string[] args) { string input; string output; input = " Have a nice day! "; Console.WriteLine("The input is: {0}", input); output = removeWhiteSpace(input); Console.WriteLine("The output without whitespace is: {0}", output); Console.ReadKey(); } '''Note how the subroutine code handles the presence of inadequate input, making it more forgiving or tolerant'''
The use of tolerant preconditions is more in accordance with the application of defensive programming techniques. The use of demanding preconditions is in agreement with the non-redundancy principle.
Applications of the Non-Redundancy Principle
Among the reasons for adhering to the non-redundancy principle we can count the following:
- Ease of maintenance due to simplified code.
- Less complexity because a particular condition is only checked once rather than in multiple places throughout the code. * The expectation is that by having components adhere to contracts that clearly define each component's responsibilities the code will be less complex.
- More reliable code because there are less places where the same validation can be incorrect.
- More reliability is expected because the number of points where failures can occur, i.e. incorrect code, is reduced.
Implications of the Non-Redundancy Principle
Although the use of the non-redundancy principle on software design has definite advantages, its application has are certain implications which must be considered. Possible consequences or implications that result from the application of the Non-Redundancy principle in a software development effort include the following:
The application of the principle may result in the introduction of vulnerabilities.
The resulting software may me more prone to having faults.
The principle is in opposition to the recommendations provided by the [Defensive Programming] techniques that are part of traditional software engineering methodologies. Defensive programming states that code should be written to include the necessary validations and preventive code to ensure its correct operation.
Supporters of the non-redundancy principle argue that the use of defensive programming techniques could possibly result in too much code that checks for errors; however, the expectation is that such additional code makes the code more robust and that it will not introduce additional faults [9].
Summary
The non-redundancy principle is used in software design to propose software where preconditions are not checked at the subroutine level but rather at the calling routine. The advantage of such design is code that is simpler and easier to maintain. The non-redundancy principle diverges from the defensive programming techniques that are proposed by traditional software engineering methodologies. One could argue that the former approach results in code that is simpler and easier to maintain while the latter results in code that is more robust and free of faults.
References
[1] http://fox.wikis.com/wc.dll?Wiki~NonRedundancyPrinciple
[2] http://fox.wikis.com/wc.dll?Wiki~PreCondition
[3] Meyer, Bertrand (1997), Object - Oriented Software Construction, Prentice Hall, Englewood Cliffs, NJ, ISBN 0136291554
[4] http://www.actapress.com/Abstract.aspx?paperId=17535
[5] http://fox.wikis.com/wc.dll?Wiki~DesignByContract
[6] http://fox.wikis.com/wc.dll?Wiki~DefensiveProgramming
[7] http://synesis.com.au/publishing/monolith/glossary.html
[8] www.cs.caltech.edu/~cs141/2003/lectures/lecture5.pdf
[9] http://movingreally.blogspot.com/2007/05/non-redundancy-principle.html
[10] Lecture slides from the Software Design course in the Department of Computer Science at the Technion---Israel institute of Technology, available at: http://www.cs.technion.ac.il/Courses/OOP/slides/export/236700/yoav/eiffel_programming_by_contract_new.ppt
CSC 517 Fall 2009
Wiki 3 Assignment
Author: Newwolf