Transcript High-Quality Programming Code Construction
Defensive Programming and Exceptions
How to Design Error Steady Code Ivaylo Bratoev Telerik Corporation www.telerik.com
Defensive Programming
Using Assertions and Exceptions Correctly
Protecting from invalid input
“Garbage in, garbage out.” – NO!
Garbage in - nothing out, error message out or no garbage allowed in
Check the values of all data from external sources (a user, a file, internet, etc.)
3
Protecting from invalid input
Check method preconditions – parameters, object state
Check method postconditions – what is guaranteed to the caller string Substring(string str, int startIndex, int length) { REQUIRE(str != NULL); preconditions REQUIRE(startIndex >= str.Length); REQUIRE(startIndex + count > str.Lenght); string result = … main logic } ENSURE(result.Length == length); postconditions.
4
Assertions
A statement placed in a program to indicate that must always be true at that place void GetAvarageStudentGrade() { Debug.Assert(studentGrades.Count > 0, “student grades are not initialized”); return studentGrades.Avarage(); }
Failed assertion indicates a fatal error in the program (usually unrecoverable)
Assertions should fail loud
5
Assertions
Assertions are used during development , they are removed during production compilation
Use assertions for conditions that should never occur
Avoid putting executable code in assertions assert PerformAction() : “Couldn't perform action”
Won’t be compiled in production. Better: bool actionedPerformed = PerformAction(); assert actionedPerformed : “Couldn't perform action”
6
Assertions
Use assertions to document preconditions and postconditions that must be true private Student GetRegisteredStudent(int id) { Debug.Assert(id > 0); Student student = registeredStudents[id]; Debug.Assert(student.IsRegistered); }
For highly robust code, assert, and then handle the error anyway
Assertions check for bugs in code
7
Error Handling Techniques
How do you handle errors that you do expect to occur?
Depends on the situation. Examples:
Return a neutral value
Return the same answer as the previous time
Log a warning message to a file
Return an error code
Call an error processing method/object
Display an error message
Shutdown
8
Robustness vs. Correctness
How will you handle error while calculating single pixel color in a computer game?
How will you handle error while calculating single pixel color in a X-Ray software?
Correctness - never returning an inaccurate result.
Robustness - always trying to do something that will allow the software to keep running.
9
Error handling strategy
Choose your error handling strategy and follow it consistently
Strongly consider using exceptions
10
Exceptions
Exceptions are a specific means by which code can pass along errors or exceptional events to the code that called it.
Methods throw exceptions: public void ReadInput(string input) { if(input == null) { throw new ArgumentNullException(“input”); } }
11
Exceptions
Use try-catch block to handle exceptions: void playNextTurn() { try { Exception thrown here … readInput(input); Code here won’t be executed … } catch(ArgumentNullException e) { console.printLine(“Hello from catch!”); } }
12
Exceptions
Use finally to execute code even if exception occurs (not supported in C++): void playNextTurn() { try { … readInput(input); Exception thrown here Code here is always executed … } finally { console.printLine(“Hello from finally!”); } }
Perfect place to perform cleanup for any resources allocated in the try block.
13
Exceptions
Use exceptions to notify other parts of the program about errors that should not be ignored
Throw an exception only for conditions that are truly exceptional
Should I throw an exception when checking for user name and password?
Don’t use exceptions as control flow mechanisms
14
Exceptions
Throw exceptions at the right level of abstraction class Employee { … public TaxId getTaxId() throws EOFException { … } } class Employee { … public TaxId getTaxId() throws EmployeeDataNotAvailable { … } }
15
Exceptions
Use descriptive error messages
Incorrect example:
Example: throw new Exception("Error!"); throw new ArgumentException("The speed should be a number " + "between " + MIN_SPEED + " and " + MAX_SPEED + ".");
Avoid empty catch blocks try { … // lots of code … } catch(Exception ex){ }
16
Exceptions
Always include the exception cause when throwing a new exception try { WithdrawMoney(account, amount); } catch (DatabaseException dbex) { throw new WithdrawException(String.Format( "Can not withdraw the amount {0} from acoount {1}", amount, account), dbex); } We include in the exceptions chain the original source of the problem.
17
Exceptions
Catch only exceptions that you are capable to process correctly
Do not catch all exceptions
Incorrect example: try { ReadSomeFile(); } catch { Console.WriteLine("File not found!"); }
What about OutOfMemoryException ?
18
Exceptions
Have an exception handling strategy for unexpected/unhandled exception:
Consider logging (log4net, log4j, log4cplus).
Display to the end users only messages that they could understand or
19
Assertions vs Exceptions
Exceptions are announcements about error condition or unusual event
Inform the caller about error or exceptional event
Can be caught and application can continue working
Assertions are fatal errors
Assertions always indicate bugs in the code
Can not be caught and processed
Application can’t continue in case of failed assertion
When in doubt – throw exception
20
Assertions vs Exceptions
string Substring(string str, int startIndex, int length) { if (str == null) { throw new NullReferenceException("Str is null."); } if (startIndex >= str.Length) { Check the input and preconditions.
throw new ArgumentException( "Invalid startIndex:" + startIndex); } if (startIndex + count > str.Length) { throw new ArgumentException("Invalid length:" + length); } … Perform the method main logic.
Debug.Assert(result.Length == length); Check the postconditions.
}
21
Error Barricades
Barricade your program to contain the damage caused by errors
Behind the barricade is “safe” area - data is valid
Validate data crossing the boundary
Consider same approach for class design
Public methods validate the data
Private methods assume the data is safe
Consider using exceptions for public methods and assertions for private
22
Debugging Aids
Don’t automatically apply production constraints to the development version
Be willing to trade speed and resource usage during development
Introduce debugging aids early
Make errors obvious during development
Asserts should abort the program
Unhandled exceptions should not be hidden
Plan for removing debugging aids
23
Being Defensive About Defensive Programming
How much defensive programming to leave in production code
Remove code that results in hard crashes
Leave in code that checks for important errors
Log errors for your technical support personnel
See that the error messages you leave in are friendly
Too much defensive programming is not good – strive for balance
24
Defensive Programming
Questions?
http://academy.telerik.com