Objects and Data Structures: How to use them wisely to write clean code? 'Clean Code' Summary Part 5
Part 5: Objects and Data Structures:
What is meant here by data structures? Data structures are special classes used to hold data only, i.e. Pure models, e.g. Kid, Car, Animal, Event, Employee, Company, Customer ...etc. Those data are usually declared as instance variables in other classes beginnings.
The methods of this class should not do any real significant work, otherwise the data structure class is not a data structure anymore!.
So mainly, the methods are getters
and setters
(i.e. accessors and mutators), usually because the instance variables are private. There is another opinion: that Data structure variables should be public, and can be accessed directly from the instance of the class, but it is debatable that the private variables concept is better.
- In that context, the data structure class, exposes its data (variables) and have no meaningful (significant) methods or functions.
- While a normal class (Called Object here by the author after Object oriented), like
MainActivity
,ListAdapter
,Calculator
,Iterator
, hides their data, and exposes their methods that work on those data.
So, We have two approaches to solve the problem in hand, i.e. use a data Structure in its purest form, while making another Object class to make operations on their data, OR, we can make the model classes as Object classes, that hides their data while exposes their methods, like in the following two examples:
First Approach:
public class Square {
public Point topLeft;
public double side;
}
public class Rectangle {
public Point topLeft;
public double height;
public double width;
}
public class Circle {
public Point center;
public double radius;
}
public class Geometry {
public final double PI = 3.141592653589793;
public double area(Object shape) throws NoSuchShapeException {
if (shape instanceof Square) {
Square s = (Square)shape;
return s.side x s.side;
}else if (shape instanceof Rectangle) {
Rectangle r = (Rectangle)shape;
return r.height x r.width;
}else if (shape instanceof Circle) {
Circle c = (Circle)shape;
return PI x c.radius x c.radius; }
throw new NoSuchShapeException(); }
}
In this solution the shapes
are data structures, while Geometry
class is Object
Advantage: If you need to add more methods you will add them only in the Geometry class (this is the time this solution should be implemented).
Disadvantage: If you need to add more Data Structures (i.e. more shapes), you will have to change all your methods in the Geometry class.
Second Approach:
public class Square implements Shape {
private Point topLeft;
private double side;
public double area() {
return side x side;
}
}
public class Rectangle implements Shape {
private Point topLeft;
private double height;
private double width;
public double area() {
return height x width;
}
}
public class Circle implements Shape {
private Point center;
private double radius;
public final double PI = 3.141592653589793;
public double area() {
return PI x radius x radius;
}
}
In this solution, there is no data Structures, only Object classes. Advantage: If you want to add more shapes, you don't have to change any of the old code. *Disadvantage_: If you want to add new methods you will have to add them in all the previous classes.
So bottom line:
Code using data structures makes it easy to add new functions without changing the existing data structures. OO code, on the other hand, makes it easy to add new classes without changing existing functions.
Also:
Code using data structures makes it hard to add new data structures because all the functions must change. while OO code makes it hard to add new functions because all the classes must change.
So in order to write clean code: avoid making Hybrid classes which have functions that do significant things, and they also have either public variables or public accessors and mutators that, for all intents and purposes, make the private variables public, tempting other external functions to use those variables the way a procedural program would use a data structure.
Data structures should be treated as data structures and different classes should be created which have the logic code and hide their internal data which are off course: instances of the data structure classes.
Also in order to write good code that have the ability to grow without problems, you have to decide whether to choose the procedural or OO way based on the problem in hand.
Some other important points to note:
- Abstraction is to be implemented in classes as possible: This is not only about declaring abstract methods or classes, it is also about hiding the inner data and implementation of a class to avoid other classes to use them, the most common form is to declare all instance variables as private, but also we can increase the level of abstraction by hiding the internal work of a class by the use of methods e.g.:
public double getFuelTankCapacityInGallons();
public double getGallonsOfGasoline();
Those two methods reveal the internal of the class more than the following method:
public double getPercentFuelRemaining();
- Train Wrecks: are to be avoided e.g.:
final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
Instead we can make it like this:
Options opts = ctxt.getOptions();
File scratchDir = opts.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();
- Active records: they are special type of data structure that have navigation methods like save and find, but they are still to be treated as data structures.
Conclusion
- Objects expose behavior and hide data. This makes it easy to add new kinds of objects without changing existing behaviors. It also makes it hard to add new behaviors to existing objects.
- Data structures expose data and have no significant behavior. This makes it easy to add new behaviors to existing data structures but makes it hard to add new data structures to existing functions.
In any given system we will sometimes want the flexibility to add new data types, and so we prefer objects for that part of the system. Other times we will want the flexibility to add new behaviors, and so in that part of the system we prefer data types and procedures. Good software developers understand these issues without prejudice and choose the approach that is best for the job at hand.
Continue to part 6