21. high-quality programming code ii
DESCRIPTION
What is High-Quality Programming Code? Code Conventions Naming Identifiers in the Source Code: Classes, Interfaces, Structures, Namespaces, Methods, Fields, Constants, etc. Formatting the Code: Formatting Methods, Formatting Classes, Formatting Loops and Conditional Statements, Using Empty Lines, Breaking Long Statements High-Quality Methods: Method Intension, Method Behavior, Strong Cohesion and Loose Coupling, Method Length, Method Parameters Efficiently Using Variables: Declaration, Initialization, Scope, Span and Lifetime Efficiently Using Constants Efficiently Using Conditional Statements Defensive Programming Documentation and Self-Documenting Code Refactoring: Improving the Existing Code Exercises: Refactoring Existing Low-Quality CodeTRANSCRIPT
High-Quality High-Quality Programming Code Programming Code Construction – Part Construction – Part
IIIIRevealing the Secrets of Self-Revealing the Secrets of Self-
Documenting CodeDocumenting Code
Svetlin NakovSvetlin NakovTelerik Telerik
CorporationCorporationwww.telerik.com
For C# DevelopersFor C# Developers
Table of ContentsTable of Contents High-Quality MethodsHigh-Quality Methods
Cohesion and CouplingCohesion and Coupling Using Variables CorrectlyUsing Variables Correctly
Scope, Span, LifetimeScope, Span, Lifetime Using Expressions CorrectlyUsing Expressions Correctly Using ConstantsUsing Constants Using Control-Flow Logic CorrectlyUsing Control-Flow Logic Correctly
Conditional StatementsConditional Statements LoopsLoops
2
Table of Contents (2)Table of Contents (2)
Defensive ProgrammingDefensive Programming Assertions and ExceptionsAssertions and Exceptions
Comments and DocumentationComments and Documentation Self-Documenting CodeSelf-Documenting Code
Code RefactoringCode Refactoring Improving the Quality ofImproving the Quality of
Existing CodeExisting Code
3
High-Quality MethodsHigh-Quality MethodsHow to Design and Implement High-How to Design and Implement High-
Quality Methods? Understanding Quality Methods? Understanding Cohesion and CouplingCohesion and Coupling
Why We Need Why We Need Methods?Methods?
Methods are important inMethods are important in software developmentsoftware development Reduce complexityReduce complexity
Divide and conquer: complex problems Divide and conquer: complex problems can be split into composition of several can be split into composition of several simple onessimple ones
Improve code readabilityImprove code readability
Small methods with good method names Small methods with good method names make the code self-documentingmake the code self-documenting
Avoid duplicating codeAvoid duplicating code
Duplicating code is hard to maintainDuplicating code is hard to maintain5
Why We Need Why We Need Methods? (2)Methods? (2)
Methods simplify software Methods simplify software developmentdevelopment Hide implementation detailsHide implementation details
Complex logic is encapsulated and Complex logic is encapsulated and hidden behind a simple interfacehidden behind a simple interface
Algorithms and data structures are Algorithms and data structures are hidden and can be transparently hidden and can be transparently replaced laterreplaced later
Increase the level of abstractionIncrease the level of abstraction
Methods address the business problem, Methods address the business problem, not the technical implementation:not the technical implementation:
6
Bank.accounts[customer].deposit(500);Bank.accounts[customer].deposit(500);
Using Methods: Using Methods: FundamentalsFundamentals
Fundamental principle of correct method Fundamental principle of correct method usage:usage:
Methods should do exactly what their names Methods should do exactly what their names saysay
Nothing lessNothing less
Nothing moreNothing more
In case of incorrect input or incorrect In case of incorrect input or incorrect preconditions, an error should be indicatedpreconditions, an error should be indicated
7
A method should do what its A method should do what its name says or should indicate an name says or should indicate an error. Any other behaviour is error. Any other behaviour is incorrect! incorrect!
Good Methods – Good Methods – ExamplesExamples
8
long Sum(int[] elements)long Sum(int[] elements){{ long sum = 0;long sum = 0; foreach (int element in elements)foreach (int element in elements) {{ sum = sum + element;sum = sum + element; }} return sum;return sum;}}
double CalcTriangleArea(double a, double b, double c)double CalcTriangleArea(double a, double b, double c){{ if (a <= 0 || b <= 0 || c <= 0)if (a <= 0 || b <= 0 || c <= 0) {{ throw new ArgumentException("Sides should be throw new ArgumentException("Sides should be positive.");positive."); }} double s = (a + b + c) / 2;double s = (a + b + c) / 2; double area = Math.Sqrt(s * (s - a) * (s - b) * (s - double area = Math.Sqrt(s * (s - a) * (s - b) * (s - c));c)); return area;return area;}}
Symptoms of Wrong Symptoms of Wrong MethodsMethods
Method that does something different than Method that does something different than its name, is wrong for at least one of these its name, is wrong for at least one of these reasons:reasons:
The method sometimes returns incorrect result The method sometimes returns incorrect result bugbug
The method returns incorrect output when its The method returns incorrect output when its input is incorrect or unusual input is incorrect or unusual low quality low quality
Acceptable for private methods onlyAcceptable for private methods only
The method does too many things The method does too many things bad cohesion bad cohesion
The method has side effects The method has side effects spaghetti code spaghetti code
Method returns strange value when an error Method returns strange value when an error condition happens condition happens it should indicate the error it should indicate the error
9
Wrong Methods – Wrong Methods – ExamplesExamples
10
long Sum(int[] elements)long Sum(int[] elements){{ long sum = 0;long sum = 0; for (int i = 0; i < elements.Length; i++)for (int i = 0; i < elements.Length; i++) {{ sum = sum + elements[i];sum = sum + elements[i]; elements[i] = 0;elements[i] = 0; }} return sum;return sum;}}
double CalcTriangleArea(double a, double b, double c)double CalcTriangleArea(double a, double b, double c){{ if (a <= 0 || b <= 0 || c <= 0)if (a <= 0 || b <= 0 || c <= 0) {{ return 0;return 0; }} double s = (a + b + c) / 2;double s = (a + b + c) / 2; double area = Math.Sqrt(s * (s - a) * (s - b) * (s - c));double area = Math.Sqrt(s * (s - a) * (s - b) * (s - c)); return area;return area;}}
Hidden side Hidden side effecteffect
Incorrect result. Incorrect result. Throw an Throw an exception exception instead.instead.
Strong CohesionStrong Cohesion Methods should have Methods should have strong cohesionstrong cohesion
Should address single task and address Should address single task and address it wellit well
Should have clear intentShould have clear intent Methods that address several tasks in Methods that address several tasks in
the same time are hard to be namedthe same time are hard to be named String cohesion is used in engineeringString cohesion is used in engineering
In computer hardware any PC In computer hardware any PC component solves a single taskcomponent solves a single task
E.g. hard disk performs a single task – E.g. hard disk performs a single task – storagestorage
11
Acceptable Types of Acceptable Types of CohesionCohesion
Functional cohesionFunctional cohesion (independent (independent function)function) Method performs certain well-Method performs certain well-
defined calculation and returns a defined calculation and returns a single resultsingle result
The entire input is passed through The entire input is passed through parameters and the entire output is parameters and the entire output is returned as resultreturned as result
No external dependencies or side No external dependencies or side effectseffects
Examples:Examples:12
Math.Sqrt(value) Math.Sqrt(value) square root square root
String.Substring(str, startIndex, length)String.Substring(str, startIndex, length)
Char.IsLetterOrDigit(ch)Char.IsLetterOrDigit(ch)
Acceptable Types of Acceptable Types of Cohesion (2)Cohesion (2)
Sequential cohesionSequential cohesion (algorithm)(algorithm) Method performs certain sequence of Method performs certain sequence of
operations to perform a single task and operations to perform a single task and achieve certain resultachieve certain result
It encapsulates an algorithmIt encapsulates an algorithm
Example:Example:
1.1.Connect to mail serverConnect to mail server
2.2.Send message headersSend message headers
3.3.Send message bodySend message body
4.4.Disconnect from the serverDisconnect from the server 13
SendEmail(recipient, subject, body)SendEmail(recipient, subject, body)
Acceptable Types of Acceptable Types of Cohesion (3)Cohesion (3)
Communicational cohesionCommunicational cohesion (common data)(common data) A set of operations used to process certain A set of operations used to process certain
data and produce a resultdata and produce a result Example:Example:
1.1.Retrieve input data from databaseRetrieve input data from database
2.2.Perform internal calculations over retrieved Perform internal calculations over retrieved datadata
3.3.Build the reportBuild the report
4.4.Format the report as Excel worksheetFormat the report as Excel worksheet
5.5.Display the Excel worksheet on the screenDisplay the Excel worksheet on the screen14
DisplayAnnualExpensesReport(int employeeId)DisplayAnnualExpensesReport(int employeeId)
Acceptable Types of Acceptable Types of Cohesion (4)Cohesion (4)
Temporal cohesion Temporal cohesion (time related (time related activities)activities) Operations that are generally not related Operations that are generally not related
but need to happen in a certain momentbut need to happen in a certain moment Examples:Examples:
1.1. Load user settingsLoad user settings
2.2. Check for updatesCheck for updates
3.3. Load all invoices from the databaseLoad all invoices from the database
Sequence of actions to handle the eventSequence of actions to handle the event15
InitializeApplication()InitializeApplication()
ButtonConfirmClick()ButtonConfirmClick()
Unacceptable CohesionUnacceptable Cohesion Logical cohesionLogical cohesion
Performs a different operation Performs a different operation depending on an input parameterdepending on an input parameter
Incorrect example:Incorrect example:
Can be acceptable in event handlers (e.g. Can be acceptable in event handlers (e.g. the the KeyDownKeyDown event in Windows Forms) event in Windows Forms)
16
object ReadAll(int operationCode)object ReadAll(int operationCode){{ if (operationCode == 1) … // Read person if (operationCode == 1) … // Read person namename else if (operationCode == 2) … // Read else if (operationCode == 2) … // Read addressaddress else if (operationCode == 3) … // Read dateelse if (operationCode == 3) … // Read date … …}}
Unacceptable CohesionUnacceptable Cohesion Coincidental cohesion Coincidental cohesion (spaghetti)(spaghetti)
Not related (random) operations are Not related (random) operations are grouped in a method for unclear reasongrouped in a method for unclear reason
Incorrect example:Incorrect example:
1.1.Prepares annual incomes report for given Prepares annual incomes report for given customercustomer
2.2.Sorts an array of integers in increasing orderSorts an array of integers in increasing order
3.3.Calculates the square root of given numberCalculates the square root of given number
4.4.Converts given MP3 file into WMA formatConverts given MP3 file into WMA format
5.5.Sends email to given customerSends email to given customer
17
HandleStuff(customerId, int[], ref sqrtValue, HandleStuff(customerId, int[], ref sqrtValue, mp3FileName, emailAddress)mp3FileName, emailAddress)
Loose CouplingLoose Coupling What is What is loose couplingloose coupling??
Minimal dependences of the method on Minimal dependences of the method on the other parts of the source codethe other parts of the source code
Minimal dependences on the class Minimal dependences on the class members or external classes and their members or external classes and their membersmembers
No side effectsNo side effects
If the coupling is loose, we can easily If the coupling is loose, we can easily reuse a method or group of methods in reuse a method or group of methods in a new projecta new project
Tight coupling Tight coupling spaghetti code spaghetti code18
Loose Coupling (2)Loose Coupling (2) The ideal couplingThe ideal coupling
A methods depends only on its A methods depends only on its parametersparameters
Does not have any other input or outputDoes not have any other input or output
Example: Example: Math.SqrtMath.Sqrt Real worldReal world
Complex software cannot avoid coupling Complex software cannot avoid coupling but could make it as loose as possiblebut could make it as loose as possible
Example: complex encryption algorithm Example: complex encryption algorithm performs initialization, encryption, performs initialization, encryption, finalizationfinalization
19
Coupling – ExampleCoupling – Example Intentionally increased coupling for Intentionally increased coupling for
more flexibility (.NET cryptography more flexibility (.NET cryptography API):API):
20
byte[] EncryptAES(byte[] inputData, byte[] byte[] EncryptAES(byte[] inputData, byte[] secretKey)secretKey){{ Rijndael cryptoAlg = new RijndaelManaged();Rijndael cryptoAlg = new RijndaelManaged(); cryptoAlg.Key = secretKey;cryptoAlg.Key = secretKey; cryptoAlg.GenerateIV();cryptoAlg.GenerateIV(); MemoryStream destStream = new MemoryStream();MemoryStream destStream = new MemoryStream(); CryptoStream csEncryptor = new CryptoStream(CryptoStream csEncryptor = new CryptoStream( destStream, cryptoAlg.CreateEncryptor(),destStream, cryptoAlg.CreateEncryptor(), CryptoStreamMode.Write);CryptoStreamMode.Write); csEncryptor.Write(inputData, 0, csEncryptor.Write(inputData, 0, inputData.Length);inputData.Length); csEncryptor.FlushFinalBlock();csEncryptor.FlushFinalBlock(); byte[] encryptedData = destStream.ToArray();byte[] encryptedData = destStream.ToArray(); return encryptedData;return encryptedData;}}
Loose Coupling – Loose Coupling – ExampleExample
To reduce coupling we can make To reduce coupling we can make utility classes that hide the complex utility classes that hide the complex logic and provide simple logic and provide simple straightforward interface (a.k.a. straightforward interface (a.k.a. façade):façade):
21
byte[] EncryptAES(byte[] inputData, byte[] byte[] EncryptAES(byte[] inputData, byte[] secretKey)secretKey){{ MemoryStream inputStream =MemoryStream inputStream = new MemoryStream(inputData);new MemoryStream(inputData); MemoryStream outputStream = new MemoryStream();MemoryStream outputStream = new MemoryStream(); EncryptionUtils.EncryptAES(EncryptionUtils.EncryptAES( inputStream, outputStream, secretKey);inputStream, outputStream, secretKey); byte[] encryptedData = outputStream.ToArray();byte[] encryptedData = outputStream.ToArray(); return encryptedData;return encryptedData;}}
Tight Coupling – Tight Coupling – ExampleExample
Passing parameters through class Passing parameters through class fieldsfields Typical example of tight couplingTypical example of tight coupling Don't do this unless you have a good Don't do this unless you have a good
reason!reason!
22
class Sumatorclass Sumator{{ public int a, b;public int a, b;
int Sum()int Sum() {{ return a + b;return a + b; }}
static void Main()static void Main() {{ Sumator sumator = new Sumator() { a = 3, b = 5 };Sumator sumator = new Sumator() { a = 3, b = 5 }; Console.WriteLine(sumator.Sum());Console.WriteLine(sumator.Sum()); }}}}
Why don't pass Why don't pass the numbers as the numbers as
parameters?parameters?
Loose Coupling and Loose Coupling and OOPOOP
Reducing coupling with OOP Reducing coupling with OOP techniquestechniques AbstractionAbstraction
Define a public interface and hide the Define a public interface and hide the implementation detailsimplementation details
EncapsulationEncapsulation
Make methods and fields private unless Make methods and fields private unless they are definitely neededthey are definitely needed
Define new members as privateDefine new members as private
Increase visibility as soon as this is Increase visibility as soon as this is neededneeded 23
Acceptable CouplingAcceptable Coupling Method is coupled to its parametersMethod is coupled to its parameters
This is the best type of couplingThis is the best type of coupling
Method in a class is coupled to some class Method in a class is coupled to some class fieldsfields
This coupling is usual, do not worry too muchThis coupling is usual, do not worry too much
Method in a class is coupled to static Method in a class is coupled to static methods, properties or constants in external methods, properties or constants in external classclass
This is normal, usually is not a problemThis is normal, usually is not a problem
Method is coupled to external static fieldsMethod is coupled to external static fields
Avoid this, classes should keep their fields Avoid this, classes should keep their fields privateprivate
24
Non-Acceptable Non-Acceptable CouplingCoupling
Method in a class is coupled to static Method in a class is coupled to static fields in external classfields in external class Avoid it Avoid it consider refactoring consider refactoring
Methods take as input data some fields Methods take as input data some fields that could be passed as parametersthat could be passed as parameters Check the intent of the methodCheck the intent of the method
Is it designed to process internal class Is it designed to process internal class data or is utility methoddata or is utility method
Method is defined public without being Method is defined public without being part of the public class's interface part of the public class's interface possible couplingpossible coupling
25
Methods ParametersMethods Parameters Put most important parameters Put most important parameters
firstfirst First put the main input parametersFirst put the main input parameters Put non-important optional Put non-important optional
parameters lastparameters last Example:Example:
Incorrect example:Incorrect example:
26
void RegisterUser(string username, string void RegisterUser(string username, string password, Date accountExpirationDate, Role[] password, Date accountExpirationDate, Role[] roles)roles)
void RegisterUser(Role[] roles, string password, void RegisterUser(Role[] roles, string password, string username, Date accountExpirationDate,)string username, Date accountExpirationDate,)
void RegisterUser(string password, Date void RegisterUser(string password, Date accountExpirationDate, Role[] roles, string accountExpirationDate, Role[] roles, string username)username)
Methods Parameters Methods Parameters (2)(2)
Do not modify the input Do not modify the input parametersparameters Use new variable insteadUse new variable instead Incorrect example:Incorrect example:
Correct example:Correct example:
27
bool CheckLogin(string username, string password)bool CheckLogin(string username, string password){{ username = username.ToLower();username = username.ToLower(); … … // Check the username / password here// Check the username / password here}}
bool CheckLogin(string username, string password)bool CheckLogin(string username, string password){{ string usernameLowercase = username.ToLower();string usernameLowercase = username.ToLower(); … … // Check the username / password here// Check the username / password here}}
Methods Parameters Methods Parameters (3)(3)
Use parameters consistentlyUse parameters consistently Use the same names and the same Use the same names and the same
order in all methodsorder in all methods
Incorrect example:Incorrect example:
Output parameters should be put Output parameters should be put lastlast
28
void EncryptFile(Stream input, Stream output, string void EncryptFile(Stream input, Stream output, string key);key);
void DecryptFile(string key, Stream output, Stream void DecryptFile(string key, Stream output, Stream input);input);
FindCustomersAndIncomes(Region region, out FindCustomersAndIncomes(Region region, out Customer[] customers, out decimal[] incomes)Customer[] customers, out decimal[] incomes)
Pass Entire Object or Pass Entire Object or Its Fields?Its Fields?
When should we pass an object When should we pass an object containing few values and when these containing few values and when these values separately?values separately? Sometime we pass an object and use only Sometime we pass an object and use only
a single field of ita single field of it – – it is a good practice?it is a good practice? Examples:Examples:
Look at the method's level of abstractionLook at the method's level of abstraction
Is it intended to operate with employees of Is it intended to operate with employees of with rates and months? with rates and months? the first is the first is incorrectincorrect 29
CalculateSalary(Employee employee, int months);CalculateSalary(Employee employee, int months);
CalculateSalary(double rate, int months);CalculateSalary(double rate, int months);
How Much Parameters How Much Parameters Methods Should Have?Methods Should Have?
Limit the number of method Limit the number of method parameters to 7 (+/-2)parameters to 7 (+/-2) 7 is a magic number in psychology7 is a magic number in psychology
Human brain cannot process more Human brain cannot process more than 7 (+/-2) things in the same timethan 7 (+/-2) things in the same time
If the parameters need to be too If the parameters need to be too much reconsider the method intent much reconsider the method intent does it have clear intent? does it have clear intent?
Consider putting few parameters in a Consider putting few parameters in a new classnew class
30
Methods LengthMethods Length How long should a method be?How long should a method be?
There is no specific restrictionThere is no specific restriction Avoid methods longer than one screen (30 Avoid methods longer than one screen (30
lines)lines) Long methods are not always badLong methods are not always bad
Be sure you have a good reason for their Be sure you have a good reason for their lengthlength
Cohesion and coupling are more important Cohesion and coupling are more important than the method length!than the method length!
Long methods often contain portions that Long methods often contain portions that could be extracted as separate methods could be extracted as separate methods with good name and clear intent with good name and clear intent check check this!this! 31
Using VariablesUsing VariablesBest PracticesBest Practices
Retuning Result from a Retuning Result from a MethodMethod
Always assign the result of a Always assign the result of a method in some variable before method in some variable before returning it. Benefits:returning it. Benefits: Improved code readabilityImproved code readability
The returned value has self-The returned value has self-documenting namedocumenting name
Simplified debuggingSimplified debugging
Example:Example:
Incorrect example:Incorrect example: 33
return days * hoursPerDay * ratePerHour;return days * hoursPerDay * ratePerHour;
int salary = days * hoursPerDay * ratePerHour;int salary = days * hoursPerDay * ratePerHour;return salary;return salary;
The intent of The intent of the formula is the formula is
obvious.obvious.
We can put a breakpoint We can put a breakpoint at this line and check if at this line and check if
the result is correct.the result is correct.
Variable InitializationVariable Initialization Initialize all variables before their first Initialize all variables before their first
usageusage Class variables (fields) are automatically Class variables (fields) are automatically
initialized with initialized with 00 / / nullnull by the C# compiler by the C# compiler You still get a warning messageYou still get a warning message
Local variables should be manually Local variables should be manually initializedinitialized This C# code will result in compilation error:This C# code will result in compilation error:
We can initialize variables at their We can initialize variables at their declaration:declaration:
34
int value;int value;Console.WriteLine(value);Console.WriteLine(value);
int value = 0;int value = 0;Console.WriteLine(value);Console.WriteLine(value);
Partially Initialized Partially Initialized ObjectsObjects
Ensure objects cannot get into Ensure objects cannot get into partially initialized statepartially initialized state Make all fields private and require Make all fields private and require
valid values for all mandatory fields valid values for all mandatory fields in all constructorsin all constructors
Example: Example: StudentStudent object is invalid object is invalid unless it has unless it has NameName and and FacultyNumberFacultyNumber
35
class Studentclass Student{{ private string name, facultyNumber;private string name, facultyNumber;
public Student(string name, string public Student(string name, string facultyNumber)facultyNumber) { … }{ … }}}
Variable ScopeVariable Scope Variable scope defines how "famous" is a Variable scope defines how "famous" is a
variable in the programvariable in the program StaticStatic variables are more "famous" than variables are more "famous" than
instanceinstance variables, and they are more "famous" variables, and they are more "famous" than than locallocal
Variables' visibility is directly related to their Variables' visibility is directly related to their scopescope publicpublic, , protectedprotected, , internalinternal, , privateprivate
Always try to reduce the variable's scopeAlways try to reduce the variable's scope This reduces potential couplingThis reduces potential coupling Avoid public fields (exception: Avoid public fields (exception: readonlyreadonly / / constconst))
Access all fields through properties / methodsAccess all fields through properties / methods36
Exceeded Scope – Exceeded Scope – ExampleExample
37
public class Globalspublic class Globals{{ public static int state = 0;public static int state = 0;}}
public class Genious public class Genious {{ public static void PrintSomething()public static void PrintSomething() {{ if (Globals.state == 0)if (Globals.state == 0) {{ Console.WriteLine("Hello.");Console.WriteLine("Hello."); }} elseelse {{ Console.WriteLine("Good bye.");Console.WriteLine("Good bye."); }} }}}}
Variable Span and Variable Span and LifetimeLifetime
Variable Variable spanspan
The average number of lines of code The average number of lines of code (LOC) between variable usages(LOC) between variable usages
Variable Variable lifetimelifetime
The number of lines of code (LOC) The number of lines of code (LOC)
between the first and the last variable between the first and the last variable
usage in a blockusage in a block Keep variable span and lifetime as Keep variable span and lifetime as
low as possiblelow as possible
38
Always define and initialize Always define and initialize variables just before their first variables just before their first use and never before it!use and never before it!
Unneeded Large Unneeded Large Variable Span and Variable Span and
LifetimeLifetime
39
int count;int count;int[] numbers = new int[100];int[] numbers = new int[100];for (int i = 0; i < numbers.Length; i++)for (int i = 0; i < numbers.Length; i++){{ numbers[i] = i;numbers[i] = i;}}count = 0;count = 0;for (int i = 0; i < numbers.Length / 2; i++)for (int i = 0; i < numbers.Length / 2; i++){{ numbers[i] = numbers[i] * numbers[i];numbers[i] = numbers[i] * numbers[i];}}for (int i = 0; i < numbers.Length; i++)for (int i = 0; i < numbers.Length; i++){{ if (numbers[i] % 3 == 0)if (numbers[i] % 3 == 0) {{ count++;count++; }}}}Console.WriteLine(count);Console.WriteLine(count);
span =19 / 4
= 4.75
lifetime
("count") = 19
Reduced Reduced Variable Span Variable Span
and Lifetimeand Lifetime
40
int[] numbers = new int[100];int[] numbers = new int[100];for (int i = 0; i < numbers.Length; i++)for (int i = 0; i < numbers.Length; i++){{ numbers[i] = i;numbers[i] = i;}}for (int i = 0; i < numbers.Length / 2; i++)for (int i = 0; i < numbers.Length / 2; i++){{ numbers[i] = numbers[i] * numbers[i];numbers[i] = numbers[i] * numbers[i];}}int count = 0;int count = 0;for (int i = 0; i < numbers.Length; i++)for (int i = 0; i < numbers.Length; i++){{ if (numbers[i] % 3 == 0)if (numbers[i] % 3 == 0) {{ count++;count++; }}}}Console.WriteLine(count);Console.WriteLine(count);
span=9 / 3 = 3
lifetime = 9
Single PurposeSingle Purpose Variables should have single purposeVariables should have single purpose
Never use a single variable for multiple Never use a single variable for multiple purposes!purposes!
Economizing memory is not an excuseEconomizing memory is not an excuse Can you choose a good name for variable Can you choose a good name for variable
that is used for several purposes?that is used for several purposes? Example: variable used to count students Example: variable used to count students
of to keep the average of their gradesof to keep the average of their grades
Proposed name: Proposed name: studentsCountOrAvgGradestudentsCountOrAvgGrade
41
Variables – Other Variables – Other SuggestionsSuggestions
Don't define variables that are not Don't define variables that are not usedused Compilers usually issues warningsCompilers usually issues warnings
Don't use variables with hidden Don't use variables with hidden purposepurpose Incorrect example:Incorrect example:
Instead use enumeration:Instead use enumeration: 42
int mode = 1;int mode = 1;……if (mode == 1) …; // Readif (mode == 1) …; // Readif (mode == 2) …; // Writeif (mode == 2) …; // Writeif (mode == 3) …; // Read and writeif (mode == 3) …; // Read and write
enum ResourceAccessMode { Read, Write, enum ResourceAccessMode { Read, Write, ReadWrite }ReadWrite }
Using Using ExpressionsExpressions
Best PracticesBest Practices
Avoid Complex Avoid Complex ExpressionsExpressions
Never use complex expressions in the Never use complex expressions in the code!code! Incorrect example:Incorrect example:
Complex expressions are evil because:Complex expressions are evil because: Make code hard to read and understand, Make code hard to read and understand,
hard to debug, hard to modify and hard to hard to debug, hard to modify and hard to maintainmaintain 44
for (int i=0; i<xCoords.length; i++) {for (int i=0; i<xCoords.length; i++) { for (int j=0; j<yCoords.length; j++) {for (int j=0; j<yCoords.length; j++) { matrix[i][j] = matrix[i][j] = matrix[xCoords[findMax(i)+1]][yCoords[findMin(j)-matrix[xCoords[findMax(i)+1]][yCoords[findMin(j)-1]] *1]] * matrix[yCoords[findMax(j)+1]][xCoords[findMin(i)-matrix[yCoords[findMax(j)+1]][xCoords[findMin(i)-1]];1]]; }}}}
What shall we do if we get What shall we do if we get at this line at this line
IndexOutOfRangeExceptionIndexOutOfRangeException??
There are 10 potential sources of There are 10 potential sources of IndexOutOfRangeExceptionIndexOutOfRangeException in this in this
expression!expression!
Simplifying Complex Simplifying Complex ExpressionsExpressions
45
for (int i = 0; i < xCoords.length; i++)for (int i = 0; i < xCoords.length; i++){{ for (int j = 0; j < yCoords.length; j++)for (int j = 0; j < yCoords.length; j++) {{ int maxStartIndex = findMax(i) + 1;int maxStartIndex = findMax(i) + 1; int minStartIndex = findMin(i) - 1;int minStartIndex = findMin(i) - 1; int minXcoord = xCoords[minStartIndex];int minXcoord = xCoords[minStartIndex]; int maxXcoord = xCoords[maxStartIndex];int maxXcoord = xCoords[maxStartIndex]; int minYcoord = yCoords[minStartIndex];int minYcoord = yCoords[minStartIndex]; int maxYcoord = yCoords[maxStartIndex];int maxYcoord = yCoords[maxStartIndex]; int newValue = int newValue = matrix[maxXcoord][minYcoord] *matrix[maxXcoord][minYcoord] * matrix[maxYcoord][minXcoord];matrix[maxYcoord][minXcoord]; matrix[i][j] = newValue;matrix[i][j] = newValue; }}}}
Using ConstantsUsing ConstantsWhen and How to Use Constants?When and How to Use Constants?
Avoid Magic Numbers and Avoid Magic Numbers and StringsStrings
What is What is magic numbermagic number or or valuevalue?? Magic numbers / values are all literals Magic numbers / values are all literals
different than different than 00, , 11, , -1-1, , nullnull and and """" (empty (empty string)string)
Avoid using magic numbers / valuesAvoid using magic numbers / values They are hard to maintainThey are hard to maintain
When change occurs, you need to modify all When change occurs, you need to modify all occurrences of the magic number / constantoccurrences of the magic number / constant
Their meaning is not obviousTheir meaning is not obvious
Example: what does the number Example: what does the number 10241024 mean?mean?
47
The Evil Magic NumbersThe Evil Magic Numbers
48
public class GeometryUtilspublic class GeometryUtils{{ public static double CalcCircleArea(double radius)public static double CalcCircleArea(double radius) {{ double area = 3.14159206 * radius * radius;double area = 3.14159206 * radius * radius; return area;return area; }} public static double CalcCirclePerimeter(double radius)public static double CalcCirclePerimeter(double radius) {{ double perimeter = 6.28318412 * radius;double perimeter = 6.28318412 * radius; return perimeter;return perimeter; }} public static double CalcElipseArea(double axis1, double public static double CalcElipseArea(double axis1, double axis2)axis2) {{ double area = 3.14159206 * axis1 * axis2;double area = 3.14159206 * axis1 * axis2; return area;return area; }}}}
Turning MagicTurning MagicNumbers into Numbers into
ConstantsConstants
49
public class GeometryUtilspublic class GeometryUtils{{ public const double PI = 3.14159206;public const double PI = 3.14159206; public static double CalcCircleArea(double radius)public static double CalcCircleArea(double radius) {{ double area = PI * radius * radius;double area = PI * radius * radius; return area;return area; }} public static double CalcCirclePerimeter(double radius)public static double CalcCirclePerimeter(double radius) {{ double perimeter = 2 * PI * radius;double perimeter = 2 * PI * radius; return perimeter;return perimeter; }} public static double CalcElipseArea(double axis1, double public static double CalcElipseArea(double axis1, double axis2)axis2) {{ double area = PI * axis1 * axis2;double area = PI * axis1 * axis2; return area;return area; }}}}
When to Use When to Use Constants?Constants?
Constants should be used in the Constants should be used in the following cases:following cases:
When we need to use numbers or When we need to use numbers or other values and their logical meaning other values and their logical meaning and value are not obviousand value are not obvious
File namesFile names
Mathematical constantsMathematical constants
Bounds and rangesBounds and ranges50
public const string SettingsFileName =public const string SettingsFileName = "ApplicationSettings.xml";"ApplicationSettings.xml";
public const double E = 2.7182818284;public const double E = 2.7182818284;
public const int READ_BUFFER_SIZE = 5 * 1024 public const int READ_BUFFER_SIZE = 5 * 1024 *1024;*1024;
When to Avoid When to Avoid Constants?Constants?
Sometime it is better to keep the Sometime it is better to keep the magic values instead of using a magic values instead of using a constantconstant Error messages and exception Error messages and exception
descriptionsdescriptions
SQL commands for database SQL commands for database operationsoperations
Titles of GUI elements (labels, Titles of GUI elements (labels, buttons, menus, dialogs, etc.)buttons, menus, dialogs, etc.)
For internationalization purposes For internationalization purposes use use resourcesresources, not constants, not constants 51
Using Using Control Control
ConstructsConstructsUsing Conditional Using Conditional
Statements and Loops Statements and Loops CorrectlyCorrectly
Using Conditional Using Conditional StatementsStatements
Always use Always use {{ and and }} for the conditional for the conditional statements body, even when it is a single statements body, even when it is a single line:line:
Why omitting the brackets could be harmful?Why omitting the brackets could be harmful?
This is misleading code + misleading This is misleading code + misleading formattingformatting
53
if (condition)if (condition){{ DoSometing();DoSometing();}}
if (condition)if (condition) DoSomething();DoSomething(); DoAnotherThing();DoAnotherThing();DoDifferentThing();DoDifferentThing();
Use Simple ConditionsUse Simple Conditions Do not use complex Do not use complex ifif conditions conditions
You can always simplify them by You can always simplify them by introducing boolean variables or boolean introducing boolean variables or boolean methodsmethods
Incorrect example:Incorrect example:
Complex boolean expressions are harmfulComplex boolean expressions are harmful
How you will find the problem if you get How you will find the problem if you get IndexOutOfRangeExceptionIndexOutOfRangeException??
54
if (x > 0 && y > 0 && x < Width-1 && y < if (x > 0 && y > 0 && x < Width-1 && y < Height-1 &&Height-1 && matrix[x, y] == 0 && matrix[x-1, y] == 0 &&matrix[x, y] == 0 && matrix[x-1, y] == 0 && matrix[x+1, y] == 0 && matrix[x, y-1] == 0 &&matrix[x+1, y] == 0 && matrix[x, y-1] == 0 && matrix[x, y+1] == 0 && !visited[x, y])matrix[x, y+1] == 0 && !visited[x, y])
Simplifying Boolean Simplifying Boolean ConditionsConditions
The last example can be easily refactored The last example can be easily refactored into self-documenting code:into self-documenting code:
Now the code is:Now the code is: Easy to read – the logic of the condition is Easy to read – the logic of the condition is
clearclear
Easy to debug – breakpoint can be put at Easy to debug – breakpoint can be put at the the ifif 55
bool inRange = bool inRange = x > 0 && y > 0 && x < Width-1 && y < Height-1;x > 0 && y > 0 && x < Width-1 && y < Height-1;bool emptyCellAndNeighbours =bool emptyCellAndNeighbours = matrix[x, y] == 0 && matrix[x-1, y] == 0 &&matrix[x, y] == 0 && matrix[x-1, y] == 0 && matrix[x+1, y] == 0 && matrix[x, y-1] == 0 &&matrix[x+1, y] == 0 && matrix[x, y-1] == 0 && matrix[x, y+1] == 0;matrix[x, y+1] == 0;if (inRange && emptyCellAndNeighbours && !if (inRange && emptyCellAndNeighbours && !visited[x, y])visited[x, y])
Avoid Deep Nesting of Avoid Deep Nesting of BlocksBlocks
Deep nesting of conditional Deep nesting of conditional statements and loops makes the statements and loops makes the code unclearcode unclear Deeply nested code is complex and Deeply nested code is complex and
hard to read and understandhard to read and understand
Usually you can extract portions of Usually you can extract portions of the code in separate methodsthe code in separate methods
This simplifies the logic of the codeThis simplifies the logic of the code
Using good method name makes the Using good method name makes the code self-documentingcode self-documenting
56
Deep Nesting – ExampleDeep Nesting – Example
57
if (maxElem != Int32.MaxValue)if (maxElem != Int32.MaxValue){{ if (arr[i] < arr[i + 1])if (arr[i] < arr[i + 1]) {{ if (arr[i + 1] < arr[i + 2])if (arr[i + 1] < arr[i + 2]) {{ if (arr[i + 2] < arr[i + 3])if (arr[i + 2] < arr[i + 3]) {{ maxElem = arr[i + 3];maxElem = arr[i + 3]; }} elseelse {{ maxElem = arr[i + 2];maxElem = arr[i + 2]; }} }} elseelse {{ if (arr[i + 1] < arr[i + 3])if (arr[i + 1] < arr[i + 3]) {{ maxElem = arr[i + 3];maxElem = arr[i + 3]; }} elseelse {{ maxElem = arr[i + 1];maxElem = arr[i + 1]; }} }} }} (continues on the next slide)
Deep Nesting – Example Deep Nesting – Example (2)(2)
58
elseelse {{ if (arr[i] < arr[i + 2])if (arr[i] < arr[i + 2]) {{ if (arr[i + 2] < arr[i + 3])if (arr[i + 2] < arr[i + 3]) {{ maxElem = arr[i + 3];maxElem = arr[i + 3]; }} elseelse {{ maxElem = arr[i + 2];maxElem = arr[i + 2]; }} }} elseelse {{ if (arr[i] < arr[i + 3])if (arr[i] < arr[i + 3]) {{ maxElem = arr[i + 3];maxElem = arr[i + 3]; }} elseelse {{ maxElem = arr[i];maxElem = arr[i]; }} }} }}}}
Avoiding Deep Nesting – Avoiding Deep Nesting – ExampleExample
59
private static int Max(int i, int j)private static int Max(int i, int j){{ if (i < j)if (i < j) {{ return j;return j; }} elseelse {{ return i;return i; }}}}
private static int Max(int i, int j, int k)private static int Max(int i, int j, int k){{ if (i < j)if (i < j) {{ int maxElem = Max(j, k);int maxElem = Max(j, k); return maxElem;return maxElem; }} elseelse {{ int maxElem = Max(i, k);int maxElem = Max(i, k); return maxElem;return maxElem; }}}} (continues on the next slide)
Avoiding Deep Nesting – Avoiding Deep Nesting – ExampleExample
60
private static int FindMax(int[] arr, int i)private static int FindMax(int[] arr, int i){{ if (arr[i] < arr[i + 1])if (arr[i] < arr[i + 1]) {{ int maxElem = Max(arr[i + 1], arr[i + 2], arr[i + int maxElem = Max(arr[i + 1], arr[i + 2], arr[i + 3]);3]); return maxElem;return maxElem; }} elseelse {{ int maxElem = Max(arr[i], arr[i + 2], arr[i + 3]);int maxElem = Max(arr[i], arr[i + 2], arr[i + 3]); return maxElem;return maxElem; }}}}
if (maxElem != Int32.MaxValue) {if (maxElem != Int32.MaxValue) { maxElem = FindMax(arr, i);maxElem = FindMax(arr, i);}}
Using Case StatementUsing Case Statement Choose the most effective ordering of cases
Put the normal (usual) case first
Order cases by frequency
Put the most unusual (exceptional) case last
Order cases alphabetically or numerically
Keep the actions of each case simple
Extract complex logic in separate methods
Use the default clause in a case statement or the last else in a chain of if-else to trap errors
61
Bad Case StatementBad Case Statement
62
void ProcessNextChar(char ch)void ProcessNextChar(char ch){{ switch (parseState)switch (parseState) {{ InTag:InTag: if (ch == ">")if (ch == ">") {{ Console.WriteLine("Found tag: {0}", tag);Console.WriteLine("Found tag: {0}", tag); text = "";text = ""; parseState = ParseState.OutOfTag;parseState = ParseState.OutOfTag; }} elseelse {{ tag = tag + ch;tag = tag + ch; }} break;break; OutOfTag:OutOfTag: … … }}}}
Improved Case Improved Case StatementStatement
63
void ProcessNextChar(char ch)void ProcessNextChar(char ch){{ switch (parseState)switch (parseState) {{ InTag:InTag: ProcessCharacterInTag(ch);ProcessCharacterInTag(ch); break;break; OutOfTag:OutOfTag: ProcessCharacterOutOfTag(ch);ProcessCharacterOutOfTag(ch); break;break; default:default: throw new Exception("Invalid parse state: " +throw new Exception("Invalid parse state: " + parseState);parseState); }}}}
Using LoopsUsing Loops Choosing the correct type of loop:Choosing the correct type of loop:
Use Use forfor loop to repeat some block of code loop to repeat some block of code a certain number of timesa certain number of times
Use Use foreachforeach loop to process each element loop to process each element of array or collectionof array or collection
Use Use whilewhile / / dodo--whilewhile loop when you don't loop when you don't know how many times a block should be know how many times a block should be repeatedrepeated
Avoid deep nesting of loopsAvoid deep nesting of loops You can extract the loop body in a new You can extract the loop body in a new
methodmethod 64
Loops: Best PracticesLoops: Best Practices Keep loops simpleKeep loops simple
This helps readers of your codeThis helps readers of your code Treat the inside of the loop as it Treat the inside of the loop as it
were a routinewere a routine Don’t make the reader look inside Don’t make the reader look inside
the loop to understand the loop the loop to understand the loop controlcontrol
Think of a loop as a black box:Think of a loop as a black box:
65
while (!inputFile.EndOfFile() && !hasErrors)while (!inputFile.EndOfFile() && !hasErrors){{
}}
(black box code)(black box code)
Defensive Defensive ProgrammingProgrammingUsing Assertions and Exceptions Using Assertions and Exceptions
CorrectlyCorrectly
Principles of Defensive Principles of Defensive ProgrammingProgramming
Fundamental principle of defensive Fundamental principle of defensive programmingprogramming
Defensive programming means:Defensive programming means:
To expect incorrect input and to handle it To expect incorrect input and to handle it correctlycorrectly
To think not only about the usual execution To think not only about the usual execution flow, but to consider also unusual situationsflow, but to consider also unusual situations
To ensure that incorrect input results to To ensure that incorrect input results to exception, not to incorrect outputexception, not to incorrect output
67
Any public method should check Any public method should check its input data, preconditions its input data, preconditions and postconditionsand postconditions
Defensive Programming Defensive Programming – Example– Example
68
string Substring(string str, int startIndex, int length)string Substring(string str, int startIndex, int length){{ if (str == null)if (str == null) {{ throw new NullReferenceException("Str is null.");throw new NullReferenceException("Str is null."); }} if (startIndex >= str.Length)if (startIndex >= str.Length) {{ throw new ArgumentException(throw new ArgumentException( "Invalid startIndex:" + startIndex);"Invalid startIndex:" + startIndex); }} if (startIndex + count > str.Length)if (startIndex + count > str.Length) {{ throw new ArgumentException("Invalid length:" + throw new ArgumentException("Invalid length:" + length);length); }} … … Debug.Assert(result.Length == length);Debug.Assert(result.Length == length);}}
Check the Check the input and input and
preconditions.preconditions.
Perform the method Perform the method main logic.main logic.
Check the Check the postconditipostconditi
ons.ons.
Exceptions – Best Exceptions – Best PracticesPractices
Choose a good name for your Choose a good name for your exception classexception class Incorrect example:Incorrect example:
Example:Example:
Use descriptive error messagesUse descriptive error messages Incorrect example:Incorrect example: Example:Example:
69
throw new Exception("File error!");throw new Exception("File error!");
throw new FileNotFoundException("Cannot find file " + throw new FileNotFoundException("Cannot find file " +
fileName);fileName);
throw new Exception("Error!");throw new Exception("Error!");
throw new ArgumentException("The speed should be a number " throw new ArgumentException("The speed should be a number "
++
"between " + MIN_SPEED + " and " + MAX_SPEED + ".");"between " + MIN_SPEED + " and " + MAX_SPEED + ".");
Exceptions Handling Exceptions Handling Best Practices (2)Best Practices (2)
Catch only exceptions that you are Catch only exceptions that you are capable to process correctlycapable to process correctly Do not catch all exceptions!Do not catch all exceptions! Incorrect example:Incorrect example:
What about What about OutOfMemoryExceptionOutOfMemoryException??70
trytry
{{
ReadSomeFile();ReadSomeFile();
}}
catchcatch
{{
Console.WriteLine("File not found!");Console.WriteLine("File not found!");
}}
Exceptions Handling Exceptions Handling Best Practices (3)Best Practices (3)
Always include the exception cause Always include the exception cause when throwing a new exceptionwhen throwing a new exception
71
trytry{{ WithdrawMoney(account, amount);WithdrawMoney(account, amount);}}catch (DatabaseException dbex)catch (DatabaseException dbex){{ throw new WithdrawException(String.Format(throw new WithdrawException(String.Format( "Can not withdraw the amount {0} from acoount "Can not withdraw the amount {0} from acoount {1}",{1}", amount, account), dbex);amount, account), dbex);}} We include in the We include in the
exceptions chain the exceptions chain the original source of the original source of the
problem.problem.
Exceptions Handling Exceptions Handling Best Practices (4)Best Practices (4)
Throw exceptions at the Throw exceptions at the corresponding level of abstractioncorresponding level of abstraction Example: Bank account withdrawal Example: Bank account withdrawal
operation could throw operation could throw InsufficientFundsExceptionInsufficientFundsException but cannot but cannot throw throw FileAccessDeniedExceptionFileAccessDeniedException
Display to the end users only Display to the end users only messages that they could messages that they could understandunderstand
72
or
Disposable ResourcesDisposable Resources Handle disposable resources with Handle disposable resources with
carecare All classes implementing All classes implementing IDisposableIDisposable should follow the should follow the try-try-finallyfinally / / usingusing pattern: pattern:
73
StreamReader reader = StreamReader reader =
new StreamReader("file.txt");new StreamReader("file.txt");
trytry
{ {
String line = String line =
reader.ReadLine(); reader.ReadLine();
}}
finallyfinally
{{
reader.Close();reader.Close();
}}
StreamReader reader = StreamReader reader =
new StreamReader(new StreamReader(
"file.txt");"file.txt");
using (reader)using (reader)
{ {
String line =String line =
reader.ReadLine(); reader.ReadLine();
}}
==
Comments and Code Comments and Code DocumentationDocumentation
The Concept of Self-Documenting CodeThe Concept of Self-Documenting Code
Effective CommentsEffective Comments Effective comments do not repeat the Effective comments do not repeat the
codecode They explain it at higher level and reveal They explain it at higher level and reveal
non-obvious detailsnon-obvious details The best software documentation is the The best software documentation is the
source code itself – keep it clean and source code itself – keep it clean and readablereadable
Self-documenting code is code that is self-Self-documenting code is code that is self-explainable and does not need commentsexplainable and does not need comments Simple design, small well named methods, Simple design, small well named methods,
strong cohesion and loose coupling, simple strong cohesion and loose coupling, simple logic, good variable names, good logic, good variable names, good formatting, … formatting, … 75
Self-Documenting CodeSelf-Documenting Code Self-documenting code Self-documenting code
fundamental principlesfundamental principles
76
The best documentation is the The best documentation is the code itself. code itself.
Do not document bad code, Do not document bad code, rewrite it!rewrite it!
Make the code self-explainable Make the code self-explainable and self-documenting, easy to and self-documenting, easy to read and understand. read and understand.
Bad Comments – Bad Comments – ExampleExample
77
public static List<int> FindPrimes(int start, int end)public static List<int> FindPrimes(int start, int end){{ // Create new list of integers// Create new list of integers List<int> primesList = new List<int>();List<int> primesList = new List<int>();
// Perform a loop from start to end// Perform a loop from start to end for (int num = start; num <= end; num++)for (int num = start; num <= end; num++) {{ // Declare boolean variable, initially true// Declare boolean variable, initially true bool prime = true;bool prime = true;
// Perform loop from 2 to sqrt(num)// Perform loop from 2 to sqrt(num) for (int div = 2; div <= Math.Sqrt(num); div++)for (int div = 2; div <= Math.Sqrt(num); div++) {{ // Check if div divides num with no remainder // Check if div divides num with no remainder if (num % div == 0)if (num % div == 0) {{ // We found a divider -> the number is not // We found a divider -> the number is not primeprime prime = false;prime = false;
// Exit from the loop// Exit from the loop break;break; }}
(continues on the next slide)
Bad Comments – Bad Comments – Example (2)Example (2)
78
// Continue with the next loop value// Continue with the next loop value }}
// Check if the number is prime// Check if the number is prime if (prime)if (prime) {{ // Add the number to the list of primes// Add the number to the list of primes primesList.Add(num);primesList.Add(num); }} }}
// Return the list of primes// Return the list of primes return primesList;return primesList;}}
Self-Documenting Code – Self-Documenting Code – ExampleExample
79
public static List<int> FindPrimes(int start, int public static List<int> FindPrimes(int start, int end)end){{ List<int> primesList = new List<int>();List<int> primesList = new List<int>(); for (int num = start; num <= end; num++)for (int num = start; num <= end; num++) {{ bool isPrime = IsPrime(num);bool isPrime = IsPrime(num); if (isPrime)if (isPrime) {{ primesList.Add(num);primesList.Add(num); }} }} return primesList;return primesList;}}
(continues on the next slide)
Good code does not Good code does not need comments. It is need comments. It is
self-explaining.self-explaining.
Self-Documenting Code –Self-Documenting Code –Example (2)Example (2)
80
private static bool IsPrime(int num)private static bool IsPrime(int num)
{{
bool isPrime = true;bool isPrime = true;
int maxDivider = Math.Sqrt(num);int maxDivider = Math.Sqrt(num);
for (int div = 2; div <= maxDivider; div++)for (int div = 2; div <= maxDivider; div++)
{{
if (num % div == 0)if (num % div == 0)
{{
// We found a divider -> the number is not prime// We found a divider -> the number is not prime
isPrime = false;isPrime = false;
break;break;
}}
}}
return isPrime;return isPrime;
}}
Good methods have good Good methods have good name and are easy to name and are easy to read and understand.read and understand.
This comment explain This comment explain non-obvious details. It non-obvious details. It
does not repeat the code.does not repeat the code.
Code RefactoringCode RefactoringImproving the Quality of the Existing Improving the Quality of the Existing
CodeCode
Code RefactoringCode Refactoring What is What is refactoringrefactoring of the source of the source
code?code? Improving the design and quality of Improving the design and quality of
existing source code without changing existing source code without changing its behaviorits behavior
Step by step process that turns the bad Step by step process that turns the bad code into good code (if possible)code into good code (if possible)
Why we need refactoring?Why we need refactoring? Code constantly changes and its quality Code constantly changes and its quality
constantly degrades (unless refactored)constantly degrades (unless refactored) Requirements often change and code Requirements often change and code
needs to be changed to follow themneeds to be changed to follow them 82
Rafactoring PatternsRafactoring Patterns When should we perform refactoring of the When should we perform refactoring of the
code?code?
Bad smells in the codeBad smells in the code indicate need of indicate need of refactoringrefactoring
Unit tests guarantee that refactoring Unit tests guarantee that refactoring does not change the behaviordoes not change the behavior
Rafactoring patternsRafactoring patterns Large repeating code fragments Large repeating code fragments e extract xtract
repeating code in separate methodrepeating code in separate method
Large methods Large methods split them logically split them logically
Large loop body or deep nesting Large loop body or deep nesting extract extract methodmethod 83
Rafactoring Patterns Rafactoring Patterns (2)(2)
Refactoring patternsRefactoring patterns Class or method has weak cohesion Class or method has weak cohesion split split
into several classes / methodsinto several classes / methods
Single change carry out changes in several Single change carry out changes in several classes classes classes have tight coupling classes have tight coupling consider redesignconsider redesign
Related data are always used together but Related data are always used together but are not part of a single class are not part of a single class group them group them in a classin a class
A method has too many parameters A method has too many parameters create create a class to groups parameters togethera class to groups parameters together
A method calls more methods from another A method calls more methods from another class than from its own class class than from its own class move it move it
84
Rafactoring Patterns Rafactoring Patterns (3)(3)
Refactoring patternsRefactoring patterns
Two classes are tightly coupled Two classes are tightly coupled merge merge them or redesign them to separate their them or redesign them to separate their responsibilitiesresponsibilities
Public non-constant fields Public non-constant fields make them make them private and define accessing propertiesprivate and define accessing properties
Magic numbers in the code Magic numbers in the code consider consider extracting constantsextracting constants
Bad named class / method / variable Bad named class / method / variable rename itrename it
Complex boolean condition Complex boolean condition split it to split it to several expressions or method callsseveral expressions or method calls 85
Rafactoring Patterns Rafactoring Patterns (4)(4)
Refactoring patternsRefactoring patterns Complex expression Complex expression split it into few split it into few
simple partssimple parts A set of constants is used as enumeration A set of constants is used as enumeration
convert it to enumeration convert it to enumeration Method logic is too complex and is hard to Method logic is too complex and is hard to
understand understand extract several more simple extract several more simple methods or even create a new classmethods or even create a new class
Unused classes, methods, parameters, Unused classes, methods, parameters, variables variables remove them remove them
Large data is passed by value without a Large data is passed by value without a good reason good reason pass it by reference pass it by reference
86
Rafactoring Patterns Rafactoring Patterns (5)(5)
Refactoring patternsRefactoring patterns Few classes share repeating functionality Few classes share repeating functionality
extract base class and reuse the common extract base class and reuse the common codecode
Different classes need to be instantiated Different classes need to be instantiated depending on configuration setting depending on configuration setting use use factoryfactory
Code is not well formatted Code is not well formatted reformat it reformat it Too many classes in a single namespace Too many classes in a single namespace
split classes logically into more namespacessplit classes logically into more namespaces Unused Unused usingusing definitions definitions remove them remove them Non-descriptive error messages Non-descriptive error messages improve improve
themthem Absence of defensive programming Absence of defensive programming add it add it 87
Code RefactoringCode RefactoringLive DemoLive Demo
ResourcesResources
The bible of high-quality software construction:The bible of high-quality software construction:
89
Code Complete, 2Code Complete, 2ndnd edition, Steve McConnell, edition, Steve McConnell, Microsoft Press, 2004, Microsoft Press, 2004, ISBN ISBN 07356196700735619670, , http://www.cc2e.comhttp://www.cc2e.com
The "High-Quality Programming The "High-Quality Programming Code" course at Telerik Academy:Code" course at Telerik Academy: http://codecourse.telerik.com
QuestionsQuestions??
QuestionsQuestions??
High-Quality High-Quality Programming Code Programming Code
Construction – Part IIConstruction – Part II
ExercisesExercises1.1. You are given a source code trying to solve the You are given a source code trying to solve the
"Game 15" puzzle. Refactor the code quality "Game 15" puzzle. Refactor the code quality without changing its functionality (bug fixes are without changing its functionality (bug fixes are allowed):allowed):a)a) Reformat the code.Reformat the code.
b)b) Give good names to all identifiers.Give good names to all identifiers.
c)c) Split the functionality into appropriate classes.Split the functionality into appropriate classes.
d)d) Refactor methods to achieve strong cohesion and Refactor methods to achieve strong cohesion and loose coupling.loose coupling.
e)e) Minimize the variables scope, span and lifetime.Minimize the variables scope, span and lifetime.
f)f) Improve the control-flow logic.Improve the control-flow logic.
g)g) Introduce constants where appropriate.Introduce constants where appropriate.
h)h) Add defensive programming where appropriate.Add defensive programming where appropriate.
91