Victor is a full stack software engineer who loves travelling and building things. Most recently
created Ewolo, a cross-platform workout logger.
The following is a list of notes taken on writing clean code, i.e. code that is maintainable and extensible.
Naming is the hardest and the most important part of writing clean code. Names should clearly express intent and the assumption here is that everyone involved in the codebase has the same cultural background which is not always the case in practice. Some general tips:
User
getById()
, save()
m_
or str
for strongly typed languagesfetch
, retrieve
and get
are
semantically equalFunctions or methods are the fundamental building blocks of programming. In fact, the internal operation of programs normally consists mostly of functions pushing data onto and popping data off the stack as they call each other. Sometimes memory needs to be allocated on the heap for data that must survive across function calls.
When a function is called, a stack frame is created to support the function's execution. The stack frame contains the function's local variables and the arguments passed to the function by its caller. The frame also contains housekeeping information that allows the called function (the callee) to return to the caller safely. The exact contents and layout of the stack vary by processor architecture and function call convention.
Some general tips on writing functions:
public int sum(int a, int b) {
int result = a + b;
resetGui(); // this is untestable and introduces a hidden dependency!
return result;
}
null
- caller will need to always check cluttering p code, consider using special
case return valuesnull
as a parameter value eitherNullable
or Optional
type is availableAn important but suble point to note in OOP is that objects hide their data behind abstractions and expose functions that operate on that data whereas data structures expose their data and have no meaningful functions. Good OOP requires knowing when to use objects and when to use data structures. Consider the following example:
public class Point {
public double x;
public double y;
}
public interface Point {
double getX();
double getY();
void setCartesian(double x, double y);
double getR();
double getTheta();
void setPolar(double r, double theta);
}
In the second Point
definition the co-ordinate system being used by the implementation is not
known and need not necessarily be cartesian nor polar!
Tips on writing clean OO code:
Map
, wrap it in a class (Sensors
)
to encapsulate the implementationFinally, procedural code makes it hard to add new data structures because all the functions must change. OO code makes it hard to add new functions because all the classes must change. Again, writing clean code requires insight as to when to use which style of programming.
Unit Tests should follow the F.I.R.S.T principle, i.e. they should be Fast, Independent of any external dependencies or manual setup, Repeatable, Self-validating (no manual checking verification) and Timely (run just before writing production code). Some general tips on writing clean tests:
The three laws of TDD:
AreaCalculator
which calculates the
total area of a collection of rectangles.
public class Rectangle {
public double width;
public double height;
}
public class AreaCalculator {
public double calculateArea(Collection<Rectangle> rectangles) {
double result = 0;
for (Rectangle r : rectangles) {
result += r.width * r.height;
}
return result;
}
}
We would now like to extend this function to calculate the area of circles as well. Our new function now looks
as follows:
public abstract class Shape { }
public class Rectangle extends Shape {
public double width;
public double height;
}
public class Circle extends Shape {
public double radius;
}
public class AreaCalculator {
public double calculateArea(Collection<Shape> shapes) {
double result = 0;
for (Shape s : shapes) {
if (s instanceof Rectangle) {
Rectangle r = (Rectangle) s;
result += r.width * r.height;
} else {
Circle c = (Circle) s;
result += c.radius * c.radius * Math.PI;
}
}
return result;
}
}
Extending this further to calculate the area of triangles now requires another modification to the calculateArea
method, i.e. it is not open for extension. We can change this by introducing an
area
method on the Shape
data structure. Our code now looks like the
following:
public abstract class Shape {
abstract double area();
}
public class Rectangle extends Shape {
public double width;
public double height;
@Override
public double area() {
return width * height;
}
}
public class Circle extends Shape {
public double radius;
@Override
public double area() {
return radius * radius * Math.PI;
}
}
public class AreaCalculator {
public double calculateArea(Collection<Shape> shapes) {
double result = 0;
for (Shape s : shapes) {
result += s.area(); // note the simplicity
}
return result;
}
}
HackerNews submission / discussion
Back to the article list.
Subscribe to get articles as they are published direct to your inbox!