high-quality programming code construction

177
High-Quality Programming Code Construction Revealing the Secrets of Self- Documenting Code Svetlin Nakov Telerik Corporation www.telerik. com For C# Developers

Upload: chelsa

Post on 23-Feb-2016

30 views

Category:

Documents


1 download

DESCRIPTION

High-Quality Programming Code Construction. Revealing the Secrets of Self-Documenting Code. For C# Developers. Svetlin Nakov. Telerik Corporation. www.telerik.com. Table of Contents. What is High-Quality Programming Code? Naming Identifiers Naming classes, methods, variables, etc. - PowerPoint PPT Presentation

TRANSCRIPT

High-Quality Programming Code Construction

High-Quality Programming Code ConstructionRevealing the Secrets of Self-Documenting CodeSvetlin Nakov

Telerik Corporation

www.telerik.comFor C# Developers

Table of ContentsWhat is High-Quality Programming Code?Naming IdentifiersNaming classes, methods, variables, etc.Code FormattingDesigning High-Quality ClassesHigh-Quality MethodsCohesion and CouplingUsing Variables CorrectlyUsing Expressions Correctly2

Table of Contents (2)Using ConstantsUsing Control-Flow Constructs CorrectlyConditional StatementsLoopsDefensive ProgrammingAssertions and ExceptionsComments and DocumentationCode Refactoring3

What is High-Quality Programming Code?

Why the Code Quality Is Important?5static void Main(){ int value=010, i=5, w; switch(value){case 10:w=5;Console.WriteLine(w);break;case 9:i=0;break; case 8:Console.WriteLine("8 ");break; default:Console.WriteLine("def ");{ Console.WriteLine("hoho ");} for (int k = 0; k < i; k++, Console.WriteLine(k - 'f'));break;} { Console.WriteLine("loop!"); }}What does this code do? Is it correct?

Why the Code Quality Is Important? (2)6static void Main(){ int value = 010, i = 5, w; switch (value) { case 10: w = 5; Console.WriteLine(w); break; case 9: i = 0; break; case 8: Console.WriteLine("8 "); break; default: Console.WriteLine("def "); Console.WriteLine("hoho "); for (int k = 0; k < i; k++, Console.WriteLine(k - 'f')) ; break; } Console.WriteLine("loop!");}Now the code is formatted, but is still unclear.

Software QualityExternal qualityDoes the software behave correctly?Are the produced results correct?Does the software run fast?Is the software UI easy-to-use?Internal qualityIt the code easy to read and understand?It the code well structured?Is the code easy to modify?7

What is High-Quality Programming Code?High-quality programming code:Easy to read and understandEasy to modify and maintainCorrect behavior in all casesWell testedWell architectured and designedWell documentedSelf-documenting code Well formatted8

What is High-Quality Programming Code? (2)High-quality programming code:Strong cohesion at all levels: modules, classes, methods, etc.Single unit is responsible for single taskLoose coupling between modules, classes, methods, etc.Units are independent one of anotherGood formattingGood names for classes, methods, variables, etc.Self-documenting code style9Code ConventionsCode conventions are formal guidelines about the style of the source code:Code formatting conventionsIndentation, whitespace, etc.Naming conventionsPascalCase or camelCase, prefixes, suffixes, etc.Best practicesClasses, interfaces, enumerations, structures, inheritance, exceptions, properties, events, constructors, fields, operators, etc.10

Code Conventions (2)Microsoft has official code conventions calledDesign Guidelines for Developing Class Libraries: http://msdn.microsoft.com/en-us/library/ms229042.aspxLarge organization follow strict conventionsCode conventions can vary in different teamsMost conventions developers follow the official Microsoft's recommendations but extend themHigh-quality code goes beyond code conventionsSoftware quality is not just a set of conventions its is a way of thinking11Managing ComplexityManaging complexity has central role in software constructionMinimize the amount of complexity that anyones brain has to deal with at certain time Architecture and design challengesDesign modules and classes to reduce complexityCode construction challengesApply good software construction practices: classes, methods, variables, naming, statements, error handling, formatting, comments, etc.12Managing Complexity (2)Key to being an effective programmer:Maximizing the portion of a program that you can safely ignore while working on any one section of codeMost practices discussed later propose ways to achieve this important goal13

Naming IdentifiersNaming Classes, Interfaces, Enumerations, Methods, Variables and Constants

General Naming GuidelinesAlways use EnglishHow you will feel if you read Vietnamese code with variables named in Vietnamese?English is the only language that all software developers speakAvoid abbreviationsExample: scrpCnt vs. scriptsCountAvoid hard-to-pronounce namesExample: dtbgRegExPtrn vs. dateTimeBulgarianRegExPattern15

Use Meaningful NamesAlways prefer using meaningful namesNames should answer these questions:What does this class do? What is the intent of this variable? What is this variable / class used for?Examples:FactorialCalculator, studentsCount, Math.PI, configFileName, CreateReportIncorrect examples: k, k2, k3, junk, f33, KJJ, button1, variable, temp, tmp, temp_var, something, someValue16

Fake Meaningful NamesJunior developers often use meaningful names that are in fact meaninglessBad naming examples:Topic6Exercise12, LoopsExercise12, Problem7, OOPLecture_LastExerciseYes, Topic6Exercise12 indicates that this is solution to exercise 12, but what is it about?Better naming:MaximalNumbersSubsequence17

Naming TypesNaming types (classes, structures, etc.)Use PascalCase character casingExamples:RecursiveFactorialCalculator, TreeSet, XmlDocument, IEnumerable, Color, TreeNode, InvalidTransactionException, MainFormIncorrect examples: recursiveFactorialCalculator, recursive_factorial_calculator, RECURSIVE_FACTORIAL_CALCULATOR18Naming Classes and StructuresUse the following formats:[Noun][Adjective] + [Noun]Examples:Student, FileSystem, BinaryTreeNode, Constants, MathUtils, CheckBox, CalendarIncorrect examples:Move, FindUsers, Fast, Optimize, Extremly, FastFindInDatabase, Check, CheckBox19

Naming InterfacesFollowing formats are acceptable:'I' + [Verb] + 'able''I' + [Noun], 'I' + [Adjective] + [Noun]Examples:IEnumerable, IFormattable, IDataReader, IList, IHttpModule, ICommandExecutorIncorrect examples:List, FindUsers, IFast, IMemoryOptimize, Optimizer, FastFindInDatabase, CheckBox20

Naming EnumerationsSeveral formats are acceptable:[Noun] or [Verb] or [Adjective]Use the same style for all membersExamples:enum Days {Monday, Tuesday, Wednesday, }, enum AppState {Running, Finished, }Incorrect examples:enum Color {red, green, blue, white}, enum PAGE_FORMAT {A4, A5, A3, LEGAL, }21

Naming Special ClassesAttributesAdd 'Attribute' as suffixExample: WebServiceAttributeIncorrect example: WebServiceCollection ClassesAdd 'Collection' as suffixExample: StringsCollectionIncorrect example: ListOfStrings22

Naming Special Classes (2)ExceptionsAdd 'Exception' as suffixUse informative nameExample: FileNotFoundExceptionIncorrect example: FileNotFoundErrorDelegate ClassesAdd 'Delegate' or 'EventHandler' as suffixExample: DownloadFinishedDelegateIncorrect example: WakeUpNotification23

The Length of Class NamesHow long could be the name of a class / struct / interface / enum?The name should be as long as requiredDon't abbreviate the names if this could make them unclearYour IDE has autocomplete, right?Examples: FileNotFoundException, CustomerSupportNotificationServiceIncorrect examples: FNFException, CustSuppNotifSrvc24

Naming NamespacesNamespaces naming guidelinesUse PascalCaseFollowing formats are acceptable:Company . Product . Component . Product . Component . Example:Telerik.WinControls.GridViewIncorrect example: Telerik_WinControlsGridView, Classes25

Naming AssembliesAssembly names should follow the root namespace in its class hierarchyExamples:Telerik.WinControls.GridView.dllOracle.DataAccess.dllInterop.CAPICOM.dllIncorrect examples:Telerik_WinControlsGridView.dllOracleDataAccess.dll26

Naming MethodsMethods naming guidelinesMethod names should be meaningfulShould answer the question:What does this method do?If you cannot find a good name for a method, think about does it have clear intentExamples: FindStudent, LoadReport, SinusIncorrect examples: Method1, DoSomething, HandleStuff, SampleMethod, DurtyHack27

Naming Methods (2)Use PascalCase character casingExample: LoadSettings, not loadSettingsPrefer the following formats:[Verb][Verb] + [Noun], [Verb] + [Adjective] + [Noun]Examples: Show, LoadSettingsFile, FindNodeByPattern, ToString, PrintListIncorrect examples: Student, Counter, White, Generator, Approximation, MathUtils28

Methods Returning a ValueMethods returning values should describe the returned valueExamples:ConvertMetersToInches, not MetersInches or Convert or ConvertUnitMeters2Inches is still acceptableCalculateSinus is good but Sinus is still acceptableEnsure that the unit of measure of obviousPrefer MeasureFontInPixels to MeasureFont29

Single Purpose of All MethodsMethods should have a single purpose!Otherwise they cannot be named wellHow to name a method that creates annual incomes report, downloads updates from internet and scans the system for viruses?CreateAnnualIncomesReportDownloadUpdatesAndScanForViruses is a nice name, right?Methods that have multiple purposes (weak cohesion) are hard to be namedNeed to be refactored instead of named30Consistency in Methods NamingUse consistent naming in the entire projectLoadFile, LoadImageFromFile, LoadSettings, LoadFont, LoadLibrary, but not ReadTextFileUse consistently the opposites at the same level of abstraction:LoadLibrary vs. UnloadLibrary, but not FreeHandleOpenFile vs. CloseFile, but not DeallocateResourceGetName vs. SetName, but not AssignName31

The Length of Method NamesHow long could be the name of a method?The name should be as long as requiredDon't abbreviateYour IDE has autocompleteExamples:LoadCustomerSupportNotificationService, CreateMonthlyAndAnnualIncomesReportIncorrect examples:LoadCustSuppSrvc, CreateMonthIncReport32

Naming Method ParametersMethod parameters namesPreferred form: [Noun] or [Adjective] + [Noun]Should be in camelCaseShould be meaningfulUnit of measure should be obviousExamples: firstName, report, usersList, fontSizeInPixels , speedKmH , fontIncorrect examples: p, p1, p2, populate, LastName, last_name, convertImage33

Naming VariablesVariable namesShould be in camelCasePreferred form: [Noun] or [Adjective] + [Noun]Should explain the purpose of the variableIf you can't find good name for a variable check if it has a single purposeException: variables with very small scope, e.g. the index variable in a 3-lines long for-loopNames should be consistent in the project34

Naming Variables ExampleExamples:firstName, report, usersList , fontSize, maxSpeed, font, startIndex, endIndex, charsCount, configSettingsXml, config, dbConnection, createUserSqlCommandIncorrect examples:foo, bar, p, p1, p2, populate, LastName, last_name, LAST_NAME, convertImage, moveMargin, MAXSpeed, _firtName, __temp, firstNameMiddlenameAndLastName35

More about Naming VariablesThe name should be addressed to the problem we solve, not to the means we use to solve itPrefer nouns from the business domain to computer termsExamples:accounts, customers, customerAddress, accountHolder, paymentPlan, vipPlayerIncorrect examples:accountsLinkedList, customersHashtable, paymentsPriorityQueue, playersArray36Naming Boolean VariablesGive boolean variables names that imply true or falseUse positive boolean variable namesIncorrect example:Examples:hasPendingPayment, customerFound, validAddress, positiveBalance, isPrimeIncorrect examples:notFound, run, programStop, player, list, findCustomerById, isUnsuccessfull37if (! notFound) { }Naming Special VariablesNaming CountersEstablish a convention, e.g. [Noun] + 'Count'Examples: ticketsCount, customersCountStateEstablish a convention, e.g. [Noun] + 'State'Examples: blogParseState, threadStateVariables with small scope and spanShort names can be used, e.g. index, i, u38

Temporary VariablesDo really temporary variables exist?All variables in a program is temporary because they is used temporary only during the program execution, right?Temporary variables can always be named better than temp or tmp:39// Swap a[i] and a[j]int temp = a[i];a[i] = a[j];a[j] = temp;// Swap a[i] and a[j]int oldValue = a[i];a[i] = a[j];a[j] = oldValue;The Length of Variable NamesHow long could be the name of a variable?Depends on the variable scope and spanMore "famous" variables should have longer and more self-explaining nameAcceptable naming examples:

Unacceptable naming examples:40for (int i=0; i 0 && y > 0 && x < Width-1 && y < Height-1 && matrix[x, y] == 0 && matrix[x-1, y] == 0 && matrix[x+1, y] == 0 && matrix[x, y-1] == 0 && matrix[x, y+1] == 0 && !visited[x, y])The last example can be easily refactored into self-documenting code:

Now the code is:Easy to read the logic of the condition is clearEasy to debug breakpoint can be put at the ifSimplifying Boolean Conditions141bool inRange = x > 0 && y > 0 && x < Width-1 && y < Height-1;bool emptyCellAndNeighbours = matrix[x, y] == 0 && matrix[x-1, y] == 0 && matrix[x+1, y] == 0 && matrix[x, y-1] == 0 && matrix[x, y+1] == 0;if (inRange && emptyCellAndNeighbours && !visited[x, y])Avoid Deep Nesting of BlocksDeep nesting of conditional statements and loops makes the code unclearDeeply nested code is complex and hard to read and understandUsually you can extract portions of the code in separate methodsThis simplifies the logic of the codeUsing good method name makes the code self-documenting142Deep Nesting Example143if (maxElem != Int32.MaxValue){ if (arr[i] < arr[i + 1]) { if (arr[i + 1] < arr[i + 2]) { if (arr[i + 2] < arr[i + 3]) { maxElem = arr[i + 3]; } else { maxElem = arr[i + 2]; } } else { if (arr[i + 1] < arr[i + 3]) { maxElem = arr[i + 3]; } else { maxElem = arr[i + 1]; } } }(continues on the next slide)Deep Nesting Example (2)144 else { if (arr[i] < arr[i + 2]) { if (arr[i + 2] < arr[i + 3]) { maxElem = arr[i + 3]; } else { maxElem = arr[i + 2]; } } else { if (arr[i] < arr[i + 3]) { maxElem = arr[i + 3]; } else { maxElem = arr[i]; } } }}Avoiding Deep Nesting Example145private static int Max(int i, int j){ if (i < j) { return j; } else { return i; }}

private static int Max(int i, int j, int k){ if (i < j) { int maxElem = Max(j, k); return maxElem; } else { int maxElem = Max(i, k); return maxElem; }}(continues on the next slide)Avoiding Deep Nesting Example146private static int FindMax(int[] arr, int i){ if (arr[i] < arr[i + 1]) { int maxElem = Max(arr[i + 1], arr[i + 2], arr[i + 3]); return maxElem; } else { int maxElem = Max(arr[i], arr[i + 2], arr[i + 3]); return maxElem; }}

if (maxElem != Int32.MaxValue) { maxElem = FindMax(arr, i);}Using Case StatementChoose the most effective ordering of casesPut the normal (usual) case firstOrder cases by frequencyPut the most unusual (exceptional) case lastOrder cases alphabetically or numericallyKeep the actions of each case simpleExtract complex logic in separate methodsUse the default clause in a case statement or the last else in a chain of if-else to trap errors147Incorrect Case Statement148void ProcessNextChar(char ch){ switch (parseState) { InTag: if (ch == ">") { Console.WriteLine("Found tag: {0}", tag); text = ""; parseState = ParseState.OutOfTag; } else { tag = tag + ch; } break; OutOfTag: }}Improved Case Statement149void ProcessNextChar(char ch){ switch (parseState) { InTag: ProcessCharacterInTag(ch); break; OutOfTag: ProcessCharacterOutOfTag(ch); break; default: throw new Exception("Invalid parse state: " + parseState); }}Using LoopsChoosing the correct type of loop:Use for loop to repeat some block of code a certain number of timesUse foreach loop to process each element of array or collectionUse while / do-while loop when you don't know how many times a block should be repeatedAvoid deep nesting of loopsYou can extract the loop body in a new method150Loops: Best PracticesKeep loops simpleThis helps readers of your codeTreat the inside of the loop as it were a routineDont make the reader look inside the loop to understand the loop controlThink of a loop as a black box:

151while (!inputFile.EndOfFile() && !hasErrors){

}(black box code)Defensive ProgrammingUsing Assertions and Exceptions Correctly

AssertionsAssertions are conditions that should always be trueUsed to check preconditions and postconditionsFailed assertion indicates a fatal error in the program (usually unrecoverable)Example:153void LoadTemplates(string fileName){ bool templatesFileExist = File.Exists(fileName); Debug.Assert(templatesFileExist, "Can't load templates file: " + fileName);}Assertions vs. ExceptionsExceptions are announcements about error condition or unusual eventInform the caller about error or exceptional eventCan be caught and application can continue its workAssertions are fatal errorsAssertions always indicate bugs in the codeCan not be caught and processedApplication cannot continue in case of failed assertion154Principles of Defensive ProgrammingFundamental principle of defensive programming

Defensive programming means:To expect incorrect input and to handle it correctlyTo think not only about the usual execution flow, but to consider also unusual situationsTo ensure that incorrect input results to exception, not to incorrect output155Any public method should check its input data, preconditions and postconditionsDefensive Programming Example156string Substring(string str, int startIndex, int length){ if (str == null) { throw new NullReferenceException("Str is null."); } if (startIndex >= str.Length) { throw new ArgumentException( "Invalid startIndex:" + startIndex); } if (startIndex + count > str.Length) { throw new ArgumentException("Invalid length:" + length); } Debug.Assert(result.Length == length);}Check the input and preconditions.Perform the method main logic.Check the postconditions.Exceptions Handling Best PracticesChoose a good name for your exception classIncorrect example:

Example:

Use descriptive error messagesIncorrect example:Example:157throw new Exception("File error!");throw new FileNotFoundException("Cannot find file " + fileName);throw new Exception("Error!");throw new ArgumentException("The speed should be a number " + "between " + MIN_SPEED + " and " + MAX_SPEED + ".");Exceptions Handling Best Practices (2)Catch only exceptions that you are capable to process correctlyDo not catch all exceptions!Incorrect example:

What about OutOfMemoryException?158try{ ReadSomeFile();}catch{ Console.WriteLine("File not found!");}Exceptions Handling Best Practices (3)Always include the exception cause when throwing a new exception159try{ 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.Exceptions Handling Best Practices (4)Throw exceptions at the corresponding level of abstractionExample: Bank account withdrawal operation could throw InsufficientFundsException but cannot throw FileAccessDeniedExceptionDisplay to the end users only messages that they could understand

160

orDisposable ResourcesHandle disposable resources with careAll classes implementing IDisposable should follow the try-finally / using pattern:161StreamReader reader = new StreamReader("file.txt");try{ String line = reader.ReadLine(); }finally{ reader.Close();}StreamReader reader = new StreamReader( "file.txt");using (reader){ String line = reader.ReadLine(); }==

Comments and Code DocumentationThe Concept of Self-Documenting CodeEffective CommentsEffective comments do not repeat the codeThey explain it at higher level and reveal non-obvious detailsThe best software documentation is the source code itself keep it clean and readableSelf-documenting code is code that is self-explainable and does not need commentsSimple design, small well named methods, strong cohesion and loose coupling, simple logic, good variable names, good formatting, 163Self-Documenting CodeSelf-documenting code fundamental principles164The best documentation is the code itself. Do not document bad code, rewrite it!Make the code self-explainable and self-documenting, easy to read and understand. Bad Comments Example165public static List FindPrimes(int start, int end){ // Create new list of integers List primesList = new List(); // Perform a loop from start to end for (int num = start; num