CSC/ECE 517 Fall 2009/wiki3 9 lkf

From Expertiza_Wiki
Jump to navigation Jump to search

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 design by contract methodology proposes the definition of the relationship between a class and its clients in terms of a formal agreement. 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]. A precondition is a property that must be true before a subroutine can be invoked.

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 [6].


        //  This method returns the square root of a floating point number
        private static double squareRoot(double input)
        {
            // The precondition for the correct execution of the Sqrt() method
            // is that the given input must not be a negative number.

            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;


            // The precondition for the correct execution of the Sqrt() method
            // is that the given input must not be a negative number.

            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();
        }
This sample contains code to verify that the input value is greater than or equal to zero. We see the same type of code both in the main() function and inside the squareRoot() method. The presence of what is essentially the same validation in both places constitutes a violation of the non-redundancy principle.


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 [2]. The agreement may be enforced or guaranteed with the use of preconditions in the form of assertions or additional code that checks for specific values. Assertions are programming constructs used to verify the validity of conditions that are expected to exist prior to the execution of a portion of code.

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;


            // The precondition for the correct execution of the Sqrt() method
            // is that the given input must not be a negative number.

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

In this code sample the validation has been removed from inside the squareRoot() method, thus eliminating the redundancy and placing the validation of the required precondition on the client code as the non-redundancy principle states.


Preconditions may be of two types: demanding and tolerant [7]. 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 [8]. 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);



            // The precondition for the correct execution of the TrimStart() and TrimEnd() methods
            // is that the given input must be a valid string.

            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();
        }
This example shows the use of code to ensure that the input is not an empty string or a null value. The presence of the validation code in main(), the client function, is what makes this an instance of a demanding precondition.


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;

            // The precondition for the correct execution of the TrimStart() and TrimEnd() methods
            // is that the given input must be a valid string.

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

In this example the validation code has been placed inside the removeWhiteSpace() method thus allowing client code to invoke it without meeting the precondition for correct execution. The presence of the validation code inside the removeWhiteSpace() method rather than in the client code constitutes an instance of a tolerant precondition.


The use of tolerant preconditions is more in accordance with the application of defensive programming techniques [9]. 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 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, 10].


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 [10, 11].



References

[1] Programming by Contract, Lecture Notes, University of San Francisco, Department of Computer Science, available at http://www.cs.usfca.edu/~parrt/course/601/lectures/programming.by.contract.html

[2] Design by Contract, Wiki page available at http://fox.wikis.com/wc.dll?Wiki~DesignByContract

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

[4] Non-RedundancyPrinciple, Wiki page available at http://fox.wikis.com/wc.dll?Wiki~NonRedundancyPrinciple

[5] Pre-Condition, Wiki page available at http://fox.wikis.com/wc.dll?Wiki~PreCondition

[6] Design by Contract, Lecture Notes, CalTech, Department of Computer Science, available at http://www.cs.caltech.edu/~cs141/2003/lectures/lecture5.pdf

[7] Programming by Contract, Lecture notes, Software Design Course, Technion---Israel institute of Technology, Department of Computer Science, available at: http://www.cs.technion.ac.il/Courses/OOP/slides/export/236700/yoav/eiffel_programming_by_contract_new.ppt

[8] Moving Forward, are we really?, Blog posting regarding the Non-redundancy principle, available at http://movingreally.blogspot.com/2007/05/non-redundancy-principle.html

[9] Defensive Programming, Wiki page available at http://fox.wikis.com/wc.dll?Wiki~DefensiveProgramming

[10] P.L. Nico, C.S. Turner, and K.K. Nico, Insecurity by Contract, Proceedings of the Software Engineering and Applications Conference, 2004, available at http://www.actapress.com/Abstract.aspx?paperId=17535

[11] Breaking Up The Monolith: Advanced C++ Design without Compromise, web resource available at http://synesis.com.au/publishing/monolith/glossary.html

[12] Computing Degrees and Careers, web resource available at http://computingcareers.acm.org/?page_id=12



CSC 517 Fall 2009

Wiki 3 Assignment

Author: Newwolf