IUST Advanced software engineering course by Dr. Saeed Parsa. Credits of slides belong to Dr. Saeed Parsa and IUST reverse engineering research laboratory. All slides are available publicly due to COVID 19 Pandemic.
3. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 3
• Make if hard for bugs to hide
• Clean code does one thing well
• Reads like well-written prose
• obscure the designer’s intent
• Provides one way rather than many ways for doing one thing
• Each routine you read turn out to be pretty much what you expect
4. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 4
• Clean Programming is some thing like martial art
• Combination of technique and art.
• The Art of Computer Programming by Knuth
• Have different school of thoughts
• Clean Programming is skill
• Good learning results in good use forever
• Changing bad learning is hard
• Like Driving!
5. Clean code does one thing well
Make it hard for bugs to hide
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 5
6. Reads like well-written prose
Never obscure the designer’s intent
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 6
7. Provides one way rather than many
ways for doing one thing
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 7
8. Each routine you read turn out to be
pretty much what you expect
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 8
9. 4/20/2020
If an object is doing more than one thing, I
break it down into two or more objects.
S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 9
In recent years I begin, and nearly end, with Beck’s rules of
simple code. In priority order, simple code:
• Runs all the tests;
• Contains no duplication;
• Expresses all the design ideas that are in the system;
• Minimizes the number of entities such as classes, methods,
functions, and the like.
Of these, I focus mostly on duplication. When the same thing is
done over and over, it’s a sign that there is an idea in our mind
that is not well represented in the code. I try to figure out what
it is. Then I try to express that idea more clearly.
10. Code is clean if it can be understood easily – by everyone on the team.
With understandability comes readability, changeability, extensibility
and maintainability.
Clean code can be read and enhanced by a developer other than its
original author.
4/20/2020 10
11. • Follow standard conventions.
• Keep it simple stupid. Simpler is always better. Reduce complexity as
much as possible.
• Boy scout rule. Leave the campground cleaner than you found it.
• Always find root cause. Always look for the root cause of a problem.
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 11
12. • Keep configurable data at high levels.
• Prefer polymorphism to if/else or switch/case.
• Separate multi-threading code.
• Prevent over-configurability.
• Use dependency injection.
• Follow Law of Demeter. A class should know only its direct
dependencies.
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 12
13. ჻ Be consistent. If you do something a certain way, do all similar things in
the same way.
჻ Use explanatory variables.
჻ Encapsulate boundary conditions. Boundary conditions are hard to
keep track of. Put the processing for them in one place.
჻ Prefer dedicated value objects to primitive type.
჻ Avoid logical dependency. Don't write methods which works correctly
depending on something else in the same class.
჻ Avoid negative conditionals.
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 13
14. Use descriptive names
• Choose descriptive and unambiguous
names.
• Make meaningful distinction.
• Use pronounceable names.
• Use searchable names.
• Replace magic numbers with named
constants.
• Avoid encodings. Don't append prefixes
or type information.
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 14
15. Use informative names
• Number-series naming (a1, a2, .. aN) is the
opposite of intentional naming.
• Such names provide no clue to the reader.
• They are noninformative. Consider intent.
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 15
public static void copyChars(char a1[], char a2[])
{ for (int i = 0; i < a1.length; i++)
{
a2[i] = a1[i];
}
}
This function reads much better when source and
destination are used for the argument names.
16. • If a name is used in multiple places give it a
search-friendly name.
• They are noninformative. Consider intent.
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 16
Use searchable names
for (int j=0; j<34; j++)
{ s += (t[j]*4)/5; }
int realDaysPerIdealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int j=0; j < NUMBER_OF_TASKS; j++)
{
int realTaskDays = taskEstimate[j] *
realDaysPerIdealDay;
int realTaskWeeks = (realdays /
WORK_DAYS_PER_WEEK);
sum += realTaskWeeks;
}
17. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 17
int d; // elapsed time in days
3. We should choose a name that specifies what is being measured and the
unit of that measurement:
int elapsedTimeInDays;
Int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;
1. The name of a variable, function, or class, should answer all the big
questions. It should tell you why it exists, what it does, and how it is
used.
2. If a name requires a comment, then the name does not reveal its
intent
Use meaningful names
18. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 18
4. Choosing names that reveal intent can
make it much easier to understand and
change code. What is the purpose of this
code?
public List<int[]> getThem()
{ List<int[]> list1 = new ArrayList<int[]>();
for (int[] x : theList)
if (x[0] == 4) list1.add(x);
return list1; }
Use meaningful names -2
19. • Small.
• Do one thing.
• Use descriptive names.
• Prefer fewer arguments.
• Have no side effects.
• Don't use flag arguments. Split method into
several independent methods that can be
called from the client without the flag.
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 19
20. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 20
How
؞ What is it that makes a function easy to read and understand?
؞ How can we make a function communicate its intent?
؞ What attributes can we give our functions that will allow a casual
reader to intuit the kind of program they live inside?
1. Small!
჻ The first rule of functions is that they should be small. How short should
your functions be?
21. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 21
public static String renderPageWithSetupsAndTeardowns
(PageData pageData, boolean isSuite) throws Exception
{
if (isTestPage(pageData))
includeSetupAndTeardownPages(pageData, isSuite);
return pageData.getHtml();
}
- Functions should not be large enough to hold nested structures.
Therefore, the indent level of a function should not be greater than
one or two.
- Functions that do one thing cannot be reasonably divided into
sections.
22. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 22
If satatements
؞ blocks within if, else, while and so on should be one line long.
؞ Probably that line should be a call.
؞ Function called within the block should have a nicely descriptive name
Switch statements
- It’s hard to make a switch statement that does one thing.
- By their nature, switch statements always do N things.
- Make sure that each switch statement is buried in a low-level class and is
never repeated.
- We do this, of course, with polymorphism.
- The following listing shows just one of the operations that might depend
on the type of employee.
23. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 23
- Payroll.java
public Money calculatePay(Employee e)
throws InvalidEmployeeType {
switch (e.type) {
case COMMISSIONED:
return calculateCommissionedPay(e);
case HOURLY:
return calculateHourlyPay(e);
case SALARIED:
return calculateSalariedPay(e);
default:
throw new InvalidEmployeeType(e.type);
}
}
24. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 24
• It very clearly does more than one thing.
• It violates the Single Responsibility Principle (SRP) because there is
more than one reason for it to change.
• it violates the Open Closed Principle8 (OCP) because it must change
whenever new types are added.
25. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 25
The ideal number of arguments for a
function is zero (niladic).
Next comes one (monadic), followed closely
by two (dyadic).
Three arguments (triadic) should be avoided
where possible.
More than three (polyadic) requires very
special justification—and then shouldn’t be
used anyway.
Using an output argument instead of a return value
for a transformation is confusing.
26. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 26
Flag arguments are ugly. Passing a boolean into a function is a truly
terrible practice.
It immediately complicates the signature of the method, loudly
proclaiming that this function does more than one thing.
It does one thing if the flag is true and another if the flag is false!.
A function with two arguments is harder to understand than a monadic
function.
For example,
writeField(name)
is easier to understand than
writeField(output-Stream, name)
27. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 27
Argument Objects
Wrap into a class of their own, more than two or three arguments.
following declarations:
- Circle makeCircle(double x, double y, double radius);
- Circle makeCircle(Point center, double radius);
Argument Lists
Functions that take variable arguments can be monads, dyads, or even
triads. But it would be a mistake to give them more arguments than that.
void monad(Integer... args);
void dyad(String name, Integer... args);
void triad(String name, int count, Integer... args);
28. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 28
Verbs and Keywords
The function and argument should form a very
nice verb/noun pair.
For example,
write(name) is very evocative.
Have No Side Effects
Side effects are lies. Your function promises to
do one thing, but it also does other hidden
things.
30. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 30
Side effects are:
1. Call to Session.initialize(), of course.
2. Call to checkPassword function, to checks the password.
- This side effect creates a temporal coupling. That is, checkPassword can only be called
at certain times (in other words, when it is safe to initialize the session). If it is called
out of order, session data may be inadvertently lost.
- Temporal couplings are confusing, especially when hidden as a side effect.
- If you must have a temporal coupling, you should make it clear in the name of the
function. In this case we might rename the function
checkPasswordAndInitializeSession, though that certainly violates “Do one thing.”
31. 6. Comments rules
• Always try to explain yourself in
code.
• Don't be redundant.
• Don't add obvious noise.
• Don't use closing brace comments.
• Don't comment out code. Just
remove.
• Use as explanation of intent.
• Use as clarification of code.
• Use as warning of consequences.
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 31
32. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 32
• Legit comments:
• legal: //copyright and authorship
• informative: //returns...
• explanation of intent: //we rank
these higher because...
• clarification
• consequence warning
• TODOs
Some comments are necessary or beneficial. We’ll look at a few that I
consider worthy of the bits they consume.
33. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 33
Eg.
// Copyright (C) 2003,2004,2005 by Object Mentor, Inc. All rights reserved.
// Released under the terms of the GNU General Public License version 2 or later.
Informative Comments
Sometimes it is useful to provide basic
information with a comment.
For example, this comment explains the
return value:
// Returns an instance of the Responder being tested.
protected abstract Responder responderInstance();
34. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 34
“Don’t Comment Bad Code – Rewrite It”.
Comments are, at best, a necessary evil.
Comments compensate for failure to express in code
• Bad comments:
• Mumbling
• Redundant comments, Misleading comments
• Mandated comments
• Journal Comments (History of Changes)
• Noise Comments
• Position Markers & Closing Brace Comments
• Commented Out Code
35. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 35
Clarification
Sometimes it is just
helpful to translate
the meaning of some
obscure argument or
return value into
something that’s
readable.
ppublic void testCompareTo() throws Exception
{
WikiPagePath a = PathParser.parse("PageA");
WikiPagePath ab = PathParser.parse("PageA.PageB");
WikiPagePath b = PathParser.parse("PageB");
WikiPagePath aa = PathParser.parse("PageA.PageA");
WikiPagePath bb = PathParser.parse("PageB.PageB");
WikiPagePath ba = PathParser.parse("PageB.PageA");
assertTrue(a.compareTo(a) == 0); // a == a
assertTrue(a.compareTo(b) != 0); // a != b
assertTrue(ab.compareTo(ab) == 0); // ab == ab
assertTrue(a.compareTo(b) == -1); // a < b
assertTrue(aa.compareTo(ab) == -1); // aa < ab
assertTrue(ba.compareTo(bb) == -1); // ba < bb
assertTrue(b.compareTo(a) == 1); // b > a
assertTrue(ab.compareTo(aa) == 1); // ab > aa
assertTrue(bb.compareTo(ba) == 1); // bb > ba
}
36. 4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 36
Warning of Consequences
Sometimes it is useful to warn other programmers about certain
consequences.
Eg,, This comment explains why a particular test case is turned off:
// Don't run unless you have some time to kill.
public void _testWithReallyBigFile()
{
writeLinesToFile(10000000);
response.setBody(testFile);
response.readyToSend(this);
String responseString = output.toString();
assertSubString("Content-Length: 1000000000",
responseString);
assertTrue(bytesSent > 1000000000);
}
37. 7. Source code structure
• Separate concepts vertically.
• Related code should appear vertically dense.
• Declare variables close to their usage.
• Dependent functions should be close.
• Similar functions should be close.
• Place functions in the downward direction.
• Keep lines short.
• Don't use horizontal alignment.
• Use white space to associate related things and disassociate weakly related.
• Don't break indentation.
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 37
38. 8. Objects and data structures
• Hide internal structure.
• Prefer data structures.
• Avoid hybrids structures (half object and half data).
• Should be small.
• Do one thing.
• Small number of instance variables.
• Base class should know nothing about their derivatives.
• Better to have many functions than to pass some code into a
function to select a behavior.
• Prefer non-static methods to static methods.
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 38
39. • 9. Tests
• One assert per test.
• Readable.
• Fast.
• Independent.
• Repeatable.
• Code smells
• Rigidity. The software is difficult to change. A small change causes a
cascade of subsequent changes.
• Fragility. The software breaks in many places due to a single change.
• Immobility. You cannot reuse parts of the code in other projects because
of involved risks and high effort.
• Needless Complexity.
• Needless Repetition.
• Opacity. The code is hard to understand.4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 39
43. Bad Smell
• A bad smell in code
• Any symptom in the source code that possibly indicates a deeper
problem.
• The term is coined by Kent Beck.
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 43
44. Bad Smells
• Duplicated Code
• Long Method
• Large Class
• Long Parameter List
• Divergent Change
• …
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 44
45. Quality vs. Bad Smells in Code
• Duplicated Code – identical or very similar code exists
in more than one location
• Long Method –method that has grown too large
• Large Class – class that has grown too large
• Long Parameter List – hard to understand/read
• Feature Envy – a class that uses methods of another
class excessively
45
Some quality aspects:
Cohesion
Comprehensibility/ Cyclomatic Complexity
Readability
Reusability4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir)
46. Code Smell: Uncommunicative Name
• Problem: Name of the method or class does not succinctly describe
what the class is for, or what the method does
• (e.g. When someone other than the developer looks at the code, they
don’t know what is going on)
• Solution: rename or rewrite it!
464/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir)
47. Bad Smells in Code
• Duplicated code
• Bad because if you modify one instance of duplicated code but not the
others, you (may) have introduced a bug!
• Long method
• long methods are more difficult to understand
• performance concerns with respect to short methods are largely obsolete
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 47
48. Bad Smells in Code
• Large Class
• Large classes try to do too much, which reduces cohesion
• Long Parameter List
• hard to understand, can become inconsistent if the same parameter chain is
being passed from method to method
• Divergent Change
• symptom: one type of change requires changing one subset of methods;
another type of change requires changing another subset
• e.g., “I have to change these three methods every time I get a new
database.”
• Related to cohesion
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 48
49. Bad Smells in Code
• Shotgun Surgery
• a change requires lots of little changes in a lot of different classes
• Feature Envy
• a method requires lots of information from some other class
• Move it closer!
• Data Clumps
• attributes that clump together (are used together) but are not part of
the same class
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 49
50. Bad Smells in Code
• Primitive Obsession
• characterized by a reluctance to use classes instead of primitive data types
• Switch Statements
• Switch statements are often duplicated in code; they can typically be replaced
by use of polymorphism (let OO do your selection for you!)
• Parallel Inheritance Hierarchies
• Similar to shotgun surgery; each time I add a subclass to one hierarchy, I
need to do it for all related hierarchies
• Note: some design patterns encourage the use of parallel inheritance hierarchies (so
they are not always bad!)
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 50
51. Bad Smells in Code
• Lazy Class
• A class that no longer “pays its way”
• e.g., may be a class that was downsized by a previous refactoring, or represented
planned functionality that did not pan out
• Speculative Generality
• “Oh, I think we need the ability to do this kind of thing someday”
• thus have all sorts of hooks and special cases to handle things that aren’t required
• Temporary Field
• An attribute of an object is only set/used in certain circumstances;
• but an object should need all of its attributes
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 51
52. Bad Smells in Code
• Message Chains
• a client asks an object for another object and then asks that
object for another object etc.
• client depends on the structure of the navigation
• any change to the intermediate relationships requires a change to
the client
• Middle Man
• If a class is delegating more than half its responsibilities to
another class, do you really need it? Involves trade-offs, some
design patterns encourage this (e.g., Decorator)
• Inappropriate Intimacy
• Pairs of classes that know too much about each other’s
implementation details (loss of encapsulation)
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 52
53. Bad Smells in Code
• Data Class (information holder)
• These are classes that have fields, getting and setting
methods for the fields, and nothing else; they are data
holders, but objects should be about data AND behavior
• Refused Bequest
• A subclass ignores most of the functionality provided by
its superclass
• Subclass may not pass the “IS-A” test
• Comments (!)
• Comments are sometimes used to hide bad code
• “…comments are often used as a deodorant”(!)
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 53
55. Installing Roslyn
• Run Visual Studio Installer
• Select Modify
• Select the Individual components tab
• Check the box for .NET Compiler Platform SDK. You'll find it at the top
under the Compilers, build tools, and runtimes section.
Optionally, you'll also want the DGML editor to display graphs in the
visualizer:
• Check the box for DGML editor. You'll find it under the Code
tools section.
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 55
56. Installing Roslyn
• To create a new analyzer project, go to File->New->Project then select
Extensibility under Templates->Visual C#. Select the Individual
components tab.
4/20/2020 S. Parsa, Associate Professor (www.parsa.iust.ac.ir) 56
Editor's Notes
The term bad smell has been coined by Kent Beck and Fowler. Code smell is any symptom in the source code of a program that possibly indicates a deeper problem. Code smells are usually not bugs—they are not technically incorrect and do not currently prevent the program from functioning. Instead, they indicate weaknesses in design that may be slowing down development or increasing the risk of bugs or failures in the future. Once you see one of these bad smalls in your code, you need to fix it. It is not fixing a bug, yet it improves the quality of the cost which is a contributing factor to the cost of development. This modification of the code to improve the structure is known as refactoring.
temporary field, e.g., orientation in a scatterplot