270 likes | 451 Views
CSE 516 Introduction to Software Engineering. Lecture 7: Software Implementation. Sanjeev Qazi sqazi@cse.ogi.edu. Software implementation. Refers to the stage that covers the low-level design and writing code.
E N D
CSE 516 Introduction to Software Engineering.Lecture 7: Software Implementation Sanjeev Qazi sqazi@cse.ogi.edu
Software implementation • Refers to the stage that covers the low-level design and writing code. • Sometimes also refers to just the “writing code” part of it, and the low-level design is then referred to as the software design phase. • In the big scheme of things, i.e., looking at the overall software lifecycle, this stage covers a small portion of the time/effort (30% ?).
Software implementation • Decisions about the programming language and environment to use needs to be done before this stage (ideally well before this stage). • In many cases, this is an easy decision. • Other decisions: • Compiler to use. • Debugger to be used. • Any other visualization tools? • For class hierarchies. • Call graphs • Performance analysis: • Quantify, gprof • Memory leak/overwrite issues • Purify • BoundsChecker
Debugging Aids – Dump method • I typically add a Dump method on all classes. • This writes out some “useful” information about the object which can help during debugging. • Dump method can take optional begin and end messages (enclosing strings). • Example, in C++: • void MyClass::Dump( const string & beginMsg = “Begin MyClass dump\n”, const string & endMsg = “End MyClass dump\n” )
DebugIOHandler • A class to help with printing debug messages. • This is useful for seeing the debug message when you want to, and turning them off when you don’t want to see them. • You can provide a finer grain control than just on/off. • I typically have low/med/high/off, although I think low/high/off is sufficient. • When you run your application, you can specify the debug level to be: • Off or • Low or • High …
DebugIOHandler • Your code would look something like: debugLo << “About to search for object “ << objA.GetName() << endl; for all collections { debugHi << “Searching collection “ << collection.GetId() << endl; … } debugLo << “Object “ << objA.GetName() << “ was found” << endl; When debug switch to application is set to high, all debug messages will get printed (high and low messages). When debug switch to application is set to low, none of the high debug messages will get printed (only low debug messages). When debug switch to application is set to OFF, no debug messages will get printed.
DebugIOHandler • Some more details: • Your class will need to make sure that it provides the capability to share the file pointer/handle between the high and low debug objects, i.e., between gDebugHi and gDebugLo objects in the example seen. • This is because you want all the debug messages to end up in the same file, and in the correct order (i.e., the order in which they are printed in your code).
SelectiveDebug • Sometimes when dealing with a huge test case, you want debugging to be turned on only under some conditions, and off otherwise, to prevent seeing too many debug messages, • Something like a SelectiveDebug class would allow you to turn on debug to a high or low level when a condition is true (or even turn debug off ). • In other words, it allows you to modify the debug level based on a condition.
SelectiveDebug • Example: for ( all signals in a collection ) { SelectiveDebug selDbg ( signalName == “XYZ” ); DBG( debugHi << “Processing signal “ << signalName << endl ); … } • Implementation note: • You need to remember to restore the original debug level • For example: • in the destructor of SelectiveDebug class.
DBG macro • Now that we use a class like the DebugIOHandler, we have control over whether we want to print the debug messages or not. • However, when you make a release version of your application, you may not want to even have the debug messages code in there for performance as well as code size reasons. • One way to do that would be to enclose the debug printing within a macro that can be compiled out during a release/optimized build.
DBG macro #ifndef NDEBUG #define DBG (x ) x #else #define DBG (x) #endif • And it would be used as : DBG(debugLo << “About to search for “ << objA.GetName() << endl ); for all collections { DBG( debugHi << “Searching collection “ << coll.GetId() << endl ); … } DBG( debugLo << “Object “ << objA.GetName() << “ was found” << endl );
Calling functions within a debugger • Some debuggers will let you call a function from within the debugger. This is very useful because you do not need to change the code to make that function call and then rebuild your application. • However, debuggers are usually not very good about calling functions with parameters. • To workaround this, you can keep your function argument list empty, and declare debug global variables (hopefully only file globals) that you can assign values to BEFORE calling the function.
Calling functions within a debugger • Example: int dbgSignalId; // debug variable void DbgFunctionA() { #ifndef NDEBUG list l1; GetFanout( dbgSignalId, l1 ); for ( all elements in l1 ) debugHi << “debug message about the element” << endl; #endif } Set dbgSignalId in the debugger before calling DbgFunctionA
Inlining functions • Function inlining is used to gain a slight performance advantage when the function call overhead is avoided. • So this is a great feature, however, you need to be careful with inlining. • Careless inlining can hurt performance rather than help. • This can happen when inlining actually increases your code size.
Inlining functions • Examples: • When you try to inline a function that has a lot of code and is called from many different places within the application. • Note: If a function is “too big”, the compiler may not inline it at all. • If your destructor has a bunch of code in it, and is declared inline, then it may be inlined at many places, resulting in increased code size. • So, inline “very small” methods.
Pass by reference/address • Keep in mind that passing by value creates a copy of the object/variable. • This is work that needs to be done at run time, and is a performance hit. • So, if your object is “big”, you should pass it by reference or address. • Can make it a constant reference to prevent unintended modification. • This is typically an issue in C/C++ programs. • Note that Java has pass by value only, where references are passed by value.
Macro side effects • Sometimes macros can have unintended side effects. • Example: #define MAX ( a, b ) ( (a) > (b) ? (a) : (b) ) Use of above macro: int x, y; x = 10; y = 12; z = MAX ( x, y ); This is ok. z = MAX ( ++ x, ++ y ); This has a side effect of incrementing y twice. So, define MAX as an inline function if you can, otherwise do not use MAX in the way shown above (the 2nd use).
Coding style guidelines • When working in a team, it is important to have some coding style guidelines so that the code has a similar look and feel. • However, these guidelines should be small in number and reasonable. • A lot of guidelines will make it hard to follow, which means they will end up not being used. • If you search on the web, you will find more style guides than you want to look at.
Organizing code - Libraries • Apart from designing class hierarchy, you also need to think about how to organize your code into libraries. • Typically there will be high cohesion among the classes/code in one library… what I mean is that the functionality offered by a library would be in the same general area. • Example: • You can have libraries offering: • geometry functions. • string related functionality (In C++ or Java, this would be provided by the standard string class). • math library. • cryptography functions.
Organizing code - Libraries • Keep in mind that it is not necessary that a library has to have a lot of code/functions/classes. • If a library is small, it will eventually grow as you add more functionality. • Same is true of classes, i.e., you should not hesitate to make a class that has very little code. • Don’t put that code in another class just because you think it is not much code. • Large classes on the other hand are not good, it usually means that there is a need to divide that funtionality into more classes.
Organizing code - Libraries • Typically you should not have cyclic dependencies among libraries. • A library A can be dependent on library B, but B should not be dependent on A. • So, you may have some base libraries that are not dependent on any other libraries (except maybe system libraries or 3rd party libraries, like string libraries or STL in C++, or various packages in Java). • And then you may have some libraries that are built on top of the base libraries, i.e., they use the functionality provided by the base libraries and provide something more. • And so on… • Note: In some cases, it may be that you end up with some cyclic dependencies, but the goal should be to not have this, or keep this limited to a very small subset of the overall code…
Code reviews • Code reviews are very important (however, often overlooked). • Number of people in code reviews should be small ( between 2-5 ). • Amount of code to be reviewed should be “reasonable”. • What reasonable is, depends on: • Complexity of code. • Reviewer’s familiarity with code and/or domain. • Provide sufficient time for others to go through the code, so they can provide meaningful feedback. • All reviewers should print out code in the same format, with line and page numbers so that references to code via line and / or page numbers are same for all reviewers.
Code reviews • Typically minor points should be marked on the printout or emailed, and major issues should be discussed. • Minor issues can be: • Bad indentation • Unclear variable name (unless it is rampant in the code) • Etc. • Major issues can be: • Possibility of null pointer dereference. • Incorrect algorithm. • Missed or incorrectly coded features (typically caught during testing, but sometimes these can be caught in reviews). • Some error conditions or exceptions not caught. • Overall lack of comments. (or too many comments)
Code reviews • Go through the code in some orderly fashion: • Example: • As for comments on a page by page basis, or 5 pages at a time. • Don’t get too deeply involved in one discussion, but take notes and discuss further offline if needed. • Don’t attack the author, you will defeat the purpose of code reviews… • Sometimes, if feasible, sending your comments to the author well before the code review can help so the author is prepared to discuss any points that need discussion.
Code reviews • And most important is: • Follow through on the results of code review. • Reviewers should not feel their time was wasted. • I don’t mean that you need to adhere to all the “advice” you get in code reviews, but you should not ignore it for lack of time, etc.