CSC/ECE 517 Fall 2009/wiki3 9 lkf

From Expertiza_Wiki
Jump to navigation Jump to search

This is currently work in progress, please do not review yet



The 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 is part of the programming by contract and design by contract software design methodologies [1, 2]. The most widely used definition of the principle is given by Meyer, who states that "under no circumstances shall the body of a routine ever test for its precondition" [3].

The purpose of this Wiki page is to describe and explain the non-redundancy principle as it applies to the field of software design. Additionally, benefits and implications of applying the principle are discussed. Examples using the C# object-oriented programming language are used to better illustrate the principle in practice.


The Non-Redundancy Principle Explained

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 [4]. A precondition is a property that must be true for a subroutine to execute successfully. 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 [5].

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();
        }

The use of tolerant preconditions is more in accordance with the application of defensive programming techniques. Note how the subroutine in the previous code handles the presence of inadequate input, making it more forgiving or tolerant. 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] programming by contract

[2] Design by Contract

[3] Meyer, Bertrand, Object - Oriented Software Construction, Prentice Hall, Englewood Cliffs, NJ, 1997.

[4] http://fox.wikis.com/wc.dll?Wiki~NonRedundancyPrinciple

[5] http://fox.wikis.com/wc.dll?Wiki~PreCondition


[] http://www.actapress.com/Abstract.aspx?paperId=17535


[6] http://fox.wikis.com/wc.dll?Wiki~DefensiveProgramming

[7] http://synesis.com.au/publishing/monolith/glossary.html

[8] http://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