Methods (functions, routines) are important in software development
Reduce complexity
Divide and conquer: complex problems are split into composition of several simple
Improve code readability
Small methods with good method names make the code self-documenting
Avoid duplicating code
Duplicating code is hard to maintain
Methods simplify software development
Hide implementation details
Complex logic is encapsulated and hidden behind a simple interface
Algorithms and data structures are hidden and can be transparently replaced later
Increase the level of abstraction
Methods address the business problem, not the technical implementation:
Using Methods: Fundamentals
Fundamental principle of correct method usage
A method should do what its name says or should indicate an
error (throw an exception). Any other behavior is incorrect!
Methods should do exactly what their names say
Nothing less (work in all possible scenarios)
Nothing more (no side effects)
In case of incorrect input or incorrect preconditions, an error should be indicated
Bad Methods – Examples
intSum(int[] elements)
{
int sum = 0;
foreach (int element in elements)
{
sum = sum + element;
}
return sum;
}
What will happen if we sum 2,000,000,000 + 2,000,000,000?
Result: -294,967,296
doubleCalcTriangleArea(double a, double b, double c)
{
double s = (a + b + c) / 2;
double area = Math.Sqrt(s * (s - a) * (s - b) * (s - c));
return area;
}
What will happen if a = b = c = -1?
What will happen if a = b = c = 1?
Both triangles will have the same size.
Good Methods – Examples
doubleCalcTriangleArea(double a, double b, double c)
{
if (a <= 0 || b <= 0 || c <= 0)
{
thrownew ArgumentException(
"Sides should be positive.");
}
double s = (a + b + c) / 2;
double area = Math.Sqrt(s * (s - a) * (s - b) * (s - c));
return area;
}
A null reference exception will be thrown implicitly → it is not meaningful
Use the correct exception handling instead:
internalobjectGetValue(string propertyName)
{
PropertyDescriptor descriptor =
this.propertyDescriptors[propertyName];
if (descriptor == null)
{
thrownew ArgumentException("Property name "
+ propertyName + " does not exists!");
}
return descriptor.GetDataBoundValue();
}
Symptoms of Wrong Methods
Method that does something different than its name is wrong for at least one of these reasons:
The method sometimes returns incorrect result → bug
The method returns incorrect output when its input is incorrect or unusual → lowquality
Could be acceptable for private methods only
The method does too many things → badcohesion
The method has side effects → spaghetticode
Method returns strange value when an error condition happens → it should indicate the error
Wrong Methods – Examples
longSum(int[] elements)
{
long sum = 0;
for (int i = 0; i < elements.Length; i++)
{
sum = sum + elements[i];
elements[i] = 0; // Hidden side effect
}
return sum;
}
doubleCalcTriangleArea(double a, double b, double c)
{
if (a <= 0 || b <= 0 || c <= 0)
{
return0; // Incorrect result
}
double s = (a + b + c) / 2;
double area = Math.Sqrt(s * (s - a) * (s - b) * (s - c));
return area;
}
Strong Cohesion
Methods should have strong cohesion
Should address single task and address it well
Should have clear intent
Methods that address several tasks in the same time are hard to be named
Strong cohesion is used in engineering
In computer hardware any PC component solves a single task
E.g. hard disk performs a single task – storage
Acceptable Types of Cohesion
Functional cohesion (independent function)
Method performs certain well-defined calculation and returns a single result
The entire input is passed through parameters and the entire output is returned as result
No external dependencies or side effects
Examples
Math.Sqrt(value), Char.IsLetterOrDigit(ch), ...
Sequential cohesion (algorithm)
Method performs certain sequence of operations to perform a single task and achieve certain result
It encapsulates an algorithm
Example:
Connect to mail server
Send message headers
Send message body
Disconnect from the server
Communicational cohesion (common data)
A set of operations used to process certain data and produce a result
Example:
Retrieve input data from database
Perform internal calculations over retrieved data
Build the report
Format the report as Excel worksheet
Display the Excel worksheet on the screen
Temporal cohesion(time related activities)
Operations that are generally not related but need to happen in a certain moment
Examples:
Load user settings
Check for updates
Load all invoices from the database
Sequence of actions to handle the event
Unacceptable Cohesion
Logical cohesion
Performs a different operation depending on an input parameter
Incorrect example:
objectReadAll(int operationCode)
{
if (operationCode == 1) … // Read person nameelseif (operationCode == 2) … // Read addresselseif (operationCode == 3) … // Read date
…
}
Can be acceptable in event handlers (e.g. the KeyDown event in Windows Forms)
Coincidental cohesion (spaghetti)
Not related (random) operations are grouped in a method for unclear reason
FindCustomersAndIncomes(Region region,
out Customer[] customers, outdecimal[] incomes)
Pass Entire Object or Its Fields?
When should we pass an object containing few values and when these values separately?
Sometime we pass an object and use only a single field of it. Is this a good practice?
Pass Entire Object or Its Fields? - Examples
decimalCalculateSalary(Employee employee, int months);
decimalCalculateSalary(double rate, int months);
Look at the method’s level of abstraction
Is it intended to operate with employees or with rates and months? → the first is incorrect
How Much Parameters Methods Should Have?
Limit the number of parameters to 7 (+/-2)
7 is a “magic” number in psychology
Human brain cannot process more than 7 (+/-2) things in the same time
If the parameters need to be too many, reconsider the method’s intent
Does it have a clear intent?
Consider extracting few of the parameters in a new method
Methods Length
How long should a method be?
There is no specific restriction
Avoid methods longer than one screen (30 lines)
Long methods are not always bad
Be sure you have a good reason for their length
Cohesion and coupling are more important than the method length!
Long methods often contain portions that could be extracted as separate methods with good name and clear intent
Pseudocode
Pseudocode can be helpful in:
Routines design
Routines coding
Code verification
Cleaning up unreachable branches in a routine
Designing in Pseudocode
What the routine will abstract i.e. the information a routine will hide?
Routine input parameters
Routine output
Preconditions
Conditions that have to be true before a routine is called
Postconditions
Conditions that have to be true after routine execution
Design Before Coding
Why it is better to spend time on design before you start coding?
The functionality may be already available in a library, so you do not need to code at all!
You need to think of the best way to implement the task considering your project requirements
If you fail on writing the code right the first time, you need to know that programmers get emotional to their code
Pseudocode Example
Routine that evaluates an aggregate expression for a
database column (e.g. Sum, Avg, Min)
Parameters: Column Name, Expression
Preconditions:
- Check whether the column exists and throw an argument
exception if not
- If the expression parser cannot parse the expression
throw an ExpressionParsingException
Routine code: Call the evaluate method on the DataView class
and return the resulting value as string
Public Routines in Libraries
Public routines in libraries and system software is hard to change
Because customers want no breaking changes
Two reasons why you need to change a public routine:
New functionality has to be added conflicting with the old features
The name is confusing and makes the usage of the library unintuitive
Design better upfront, or refactor carefully
Method Deprecation
Deprecated method
About to be removed in future versions
When deprecating an old method
Include that in the documentation
Specify the new routine that has to be used
Use the [Obsolete] attribute in .NET
[Obsolete("CreateXml() method is deprecated.
Use CreateXmlReader instead.")]
public void CreateXml (…)
{
…
}
Conclusion
Designing and coding routines is engineering activity
There is no perfect solution
Competing solutions usually demonstrate different trade-offs
The challenge of the programmer is to
Evaluate the requirements
Choose the most appropriate solution from the available options