On reusability (Part 1) ...Misconceptions
/**
* This isn't OO
*/
public class Numbers
{
/*
public static double multiplyBy1(double number)
public static double multiplyBy10(double number)
public static double multiplyBy100(double number)
public static double multiplyBy1000(double number)
*/
public static double multiplyBy(double number, double multiplier){
return number * multiplier;
}
/*
public static double add1(double number)
public static double add10(double number)
public static double add100(double number)
public static double add1000(double number)
*/
public static double add(double number, double value){
return number + value;
}
}
/**
* Naive identification of a class
*/
public class Number
{
private final double number;
public Number(double value)
{
this.number = value;
}
/*
public double multiplyBy1()
public double multiplyBy10()
public double multiplyBy100()
public double multiplyBy1000()
*/
public double multiplyBy(double number, double multiplier){
return number * multiplier;
}
/*
public double add1()
public double add10()
public double add100()
public double add1000()
*/
public double add(double number, double value){
return number + value;
}
}
/**
* This is the result of failing to identify a class in {@link Numbers}
* that models a math operation thus having to handle different types of
* formulas.
*/
public class AbusingTheIfFormula
{
public static final int AREA_OF_CIRCLE = 1;
public static final int AREA_OF_TRIANGLE = 2;
public static final int N = 3;
private final double base;
/**
* On which type does it make sense?
*/
public AbusingTheIfFormula()
{
this.base = 0.0;
}
/**
* @param base only related to a specific type
*/
public AbusingTheIfFormula(double base)
{
this.base = base;
}
/**
* You are forced to abuse the if now
*/
public double op(double number, int type)
{
if(AREA_OF_CIRCLE == type){
number = Numbers.multiplyBy(number, Math.PI);
number = Numbers.multiplyBy(number, 2);
return number;
}
else if(AREA_OF_TRIANGLE == type)
{
if(this.base != 0.0)
{
number = Numbers.multiplyBy(base, number);
return number / 2;
}
}
else if(N == type)
{
double original = number;
number = Numbers.add(number, 1);
number = Numbers.multiplyBy(original, number);
return number / 2;
}
throw new UnsupportedOperationException("Uknown type: " + type);
}
}
/**
* Trying to come up with a way out of this mess
*/
/**
* An interface is forced to handle different kinds of formulas since
* we failed to identify an Operation class from {@link Numbers}
* that we can actually reuse.
*/
public interface Formula
{
public double op(double number);
}
/**
* Is it really about different formula implementations? Unfortunately since
* there isn't an <b>operation class</b> to have a relationship with a
* formula one, the only way is to implement different classes for different
* formulas. Naively "reusing" {@link Numbers#multiplyBy(double, double)}
* affecting any class related to it while still losing on actual reusability of
* operations.
*/
public class Formulas
{
public static class AreaOfCircleFormula implements Formula
{
public double op(double radius)
{
double value = Numbers.multiplyBy(radius, 2);
return Numbers.multiplyBy(value, Math.PI);
}
}
public static class AreaOfTriangleFormula implements Formula
{
private final double height;
public AreaOfTriangleFormula(double height)
{
this.height = height;
}
public double op(double base)
{
double value = Numbers.multiplyBy(base, this.height);
return value / 2;
}
}
public static class NFormula implements Formula
{
public double op(double n)
{
double value = Numbers.add(n, 1);
value = Numbers.multiplyBy(value, n);
return value / 2;
}
}
}
Or why extracting a method is overrated and abused
One of the most frequent [eureka][7] moments we get as developers when writing code is when finding a method to extract in order to reuse it.
In an OO world however there is only as much gain since you can only use that method within that class. The problem resides in that a function is not a first class citizen.
Simply put, you need to carry the class that defines that method in every place you want to use it. This is easier said than done though depending on how you’ve designed your class. Especially if you’ve assigned more than [1 responsibilities][8] to it.
So what you end up doing “at best” is introduce a static method, at worst copy and paste. There is a real issue here though and that is failing to identify a class that has manifest in your code leading to an explosion of methods.
This not only makes your API hard to use but also forces you to write more, sometimes even complicated code. Solely on the fact that in an OO world you have the concept of class relationships which is equally important to the class.
When you see procedural code, you only think procedural. So next time you start coding, look for the class!
Try it with your code to see what objects you come up with and how a correctly identified class simplifies things.
[7]: http://en.wikipedia.org/wiki/Eureka_(word) “This exclamation is most famously attributed to the ancient Greek scholar Archimedes; he reportedly proclaimed “Eureka!” when he stepped into a bath and noticed that the water level rose — he suddenly understood that the volume of water displaced must be equal to the volume of the part of his body he had submerged.” [8]: http://en.wikipedia.org/wiki/Single_responsibility_principle “The single responsibility principle states that every object should have a single responsibility, and that responsibility should be entirely encapsulated by the class”