Programming is a very abstract problem thus redefining the way the problem looks makes a tremendous difference. This article shows a few ways you can clean up your code in order to make solving hard software problems easier.
Imagine the following class in java for factoring integers:
public class Factor
{
public static void main(String[] args)
{
int c = 32;
System.out.println("Factors: ");
int i = 2;
while (i < c)
{
if (c % i == 0)
{
System.out.println(i + " ");
ck = ck * i;
i = 2;
c = c / i;
}
else
{
i++;
}
}
}
}
Note: This code has bugs, represents poor coding style, lack of object orientated or functional programming principles and is better done using other algorithms.
Option 1 – Create Independently Verifiable FunctionsI personally have found that if my methods are short and concise then I can simply visually inspect the correctness of each module. In the case of the integer-factoring program above the only way to verify anything is correct is to verity the entire program. This is a problem especially if you are stuck and are not sure what is wrong already. As a result of creating some targeted independently verifiable methods we now will know at least a small part of our program works. I find this extremely helpful in relieving the feeling of hopelessness that can often arise when trying to solve hard programming problems.
import java.util.List;
import java.util.ArrayList;
public class Factor
{
public static void main(String[] args)
{
int c = 32;
System.out.println("Factors: ");
System.out.println(fAll(c).toString());
}
public static List fAll(int a)
{
ArrayList res = new ArrayList();
while (a >= 2)
{
int r = f1(a);
a = a / r;
res.add(r);
}
return res;
}
public static int f1(int a)
{
int i = 2;
while (i <= a)
{
if (a % i == 0)
{
return i;
}
i++;
}
return -1;
}
}
As a result of adding the factor function we are now able to visually verity that our factor algorithm works correctly. We can run the test cases of 2, 3, 4 in our head and see that function f() returns the correct result.
Option 2 – Give Variables and Functions Meaningful NamesEven though we have modularized, our application all the function names and variables are rather foreign and hard to understand intuitively. If we change give them meaningful names we will likely have an easier time reading the code.
import java.util.List;
import java.util.ArrayList;
public class Factor
{
private static final int kFirstPrime = 2;
public static void main(String[] args)
{
int intToFactor = 32;
System.out.println("Factors: ");
System.out.println(getFactorsForInteger(intToFactor).toString());
}
public static List getFactorsForInteger(int intToFactor)
{
ArrayList factorList = new ArrayList();
while (intToFactor >= kFirstPrime)
{
int factorFound = findNextFactor(intToFactor);
intToFactor = intToFactor / factorFound;
factorList.add(factorFound);
}
return factorList;
}
public static int findNextFactor(int intToFactor)
{
int trialFactor = kFirstPrime;
while (trialFactor <= intToFactor)
{
if (intToFactor % trialFactor == 0)
{
return trialFactor;
}
trialFactor++;
}
return -1;
}
}
Notice this is the exact same code as above yet is much more readable.
Option 3 – Add Pseudocode Comments before Each Control Structure and MethodIf the problem you are trying to solve is especially hard, it might be useful to add pseudocode comments before every control branch in order to verify that you really know what you are doing. I often find it is one thing to code and another to explain what the code does.
import java.util.List;
import java.util.ArrayList;
/**
* Provides a class to factor integers.
* @author The Software Engineer
*/
public class Factor
{
/** Integer value "2" the first prime number */
private static final int kFirstPrime = 2;
/**
* Executes program and returns the integer factors of variable intToFactor.
* @param args none
*/
public static void main(String[] args)
{
//Integer to factor
int intToFactor = 32;
//Output factor results
System.out.println("Factors: ");
System.out.println(getFactorsForInteger(intToFactor).toString());
}
/**
* Returns a list of integer factors.
* @param intToFactor the integer to factor
* @return returns list of integer factors
*/
public static List getFactorsForInteger(int intToFactor)
{
//Holds all factors found
ArrayList factorList = new ArrayList();
//Loop until all the factors of the target integer have been removed
while (intToFactor >= kFirstPrime)
{
//Determine the next factor in the target integer
int factorFound = findNextFactor(intToFactor);
//Remove factor from the target
intToFactor = intToFactor / factorFound;
//Add factor to list of factors found
factorList.add(factorFound);
}
//Return list of factors
return factorList;
}
/**
* Finds the first factor starting from 2 and returns it.
* @param intToFactor integer to find next factor of
* @return factor or -1 on error
*/
public static int findNextFactor(int intToFactor)
{
//Set the trial factor to the first prime number
int trialFactor = kFirstPrime;
//Loop while a factor has not been found
while (trialFactor <= intToFactor)
{
//If the trialFactor has no remandor then we know it is a factor
if (intToFactor % trialFactor == 0)
{
//Return the factor found
return trialFactor;
}
//Check to see if next integer is a factor
trialFactor++;
}
//Never should reach here ERROR
return -1;
}
}