下面的函数用来打印1000个质数。
public class PrintPrimes {
public static void main(String[] args) {
final int M = 1000;
final int RR = 50;
final int CC = 4;
final int WW = 10;
final int ORDMAX = 30;
int P[] = new int[M + 1];
int PAGENUMBER;
int PAGEOFFSET;
int ROWOFFSET;
int C;
int J;
int K;
boolean JPRIME;
int ORD;
int SQUARE;
int N;
int MULT[] = new int[ORDMAX + 1];
J = 1;
K = 1;
P[1] = 2;
ORD = 2;
SQUARE = 9;
while (K < M) {
do {
J = J + 2;
if (J == SQUARE) {
ORD = ORD + 1;
SQUARE = P[ORD] * P[ORD];
MULT[ORD - 1] = J;
}
N = 2;
JPRIME = true;
while (N < ORD && JPRIME) {
while (MULT[N] < J)
MULT[N] = MULT[N] + P[N] + P[N];
if (MULT[N] == J)
JPRIME = false;
N = N + 1;
}
} while (!JPRIME);
K = K + 1;
P[K] = J;
}
{
PAGENUMBER = 1;
PAGEOFFSET = 1;
while (PAGEOFFSET <= M) {
System.out.println("The First " + M + " Prime Numbers --- Page " + PAGENUMBER);
System.out.println("");
for (ROWOFFSET = PAGEOFFSET; ROWOFFSET < PAGEOFFSET + RR; ROWOFFSET++) {
for (C = 0; C < CC; C++)
if (ROWOFFSET + C * RR <= M)
System.out.format("%10d", P[ROWOFFSET + C * RR]);
System.out.println("");
}
System.out.println("\f");
PAGENUMBER = PAGENUMBER + 1;
PAGEOFFSET = PAGEOFFSET + RR * CC;
}
}
}
}
在一个函数中完成这个任务,存在的问题: 比较深的缩进嵌套层次导、一些难以理解的变量名称、和一些紧密耦合的代码块,导致该代码阅读起来很困难, 如果使用小函数并且结合内的高内聚特性重构该功能,其代码将变得更易阅读,如下:
import java.io.PrintStream;
import java.util.ArrayList;
public class PrintPrimes {
public static void main(String[] args) {
final int NUMBER_OF_PRIMES = 1000;
int[] primes = PrimeGenerator.generate(NUMBER_OF_PRIMES);
final int ROWS_PER_PAGE = 50;
final int COLUMNS_PER_PAGE = 4;
RowColumnPagePrinter tablePrinter = new RowColumnPagePrinter(ROWS_PER_PAGE,
COLUMNS_PER_PAGE, "The First " + NUMBER_OF_PRIMES + " Prime Numbers");
tablePrinter.print(primes);
}
}
class RowColumnPagePrinter {
private int mRowsPerPage;
private int mColumnsPerPage;
private int mNumbersPerPage;
private String mPageHeader;
private PrintStream mPrintStream;
public RowColumnPagePrinter(int rowsPerPage, int columnsPerPage, String pageHeader) {
mRowsPerPage = rowsPerPage;
mColumnsPerPage = columnsPerPage;
mPageHeader = pageHeader;
mNumbersPerPage = rowsPerPage * columnsPerPage;
mPrintStream = System.out;
}
public void print(int data[]) {
int pageNumber = 1;
for (int firstIndexOnPage = 0; firstIndexOnPage < data.length; firstIndexOnPage += mNumbersPerPage) {
int lastIndexOnPage = Math.min(firstIndexOnPage + mNumbersPerPage - 1, data.length - 1);
printPageHeader(mPageHeader, pageNumber);
printPage(firstIndexOnPage, lastIndexOnPage, data);
mPrintStream.println("\f");
pageNumber++;
}
}
private void printPage(int firstIndexOnPage, int lastIndexOnPage, int[] data) {
int firstIndexOfLastRowOnPage = firstIndexOnPage + mRowsPerPage - 1;
for (int firstIndexInRow = firstIndexOnPage; firstIndexInRow <= firstIndexOfLastRowOnPage; firstIndexInRow++) {
printRow(firstIndexInRow, lastIndexOnPage, data);
mPrintStream.println("");
}
}
private void printRow(int firstIndexInRow, int lastIndexOnPage, int[] data) {
for (int column = 0; column < mColumnsPerPage; column++) {
int index = firstIndexInRow + column * mRowsPerPage;
if (index <= lastIndexOnPage)
mPrintStream.format("%10d", data[index]);
}
}
private void printPageHeader(String pageHeader, int pageNumber) {
mPrintStream.println(pageHeader + " --- Page " + pageNumber);
mPrintStream.println("");
}
public void setOutput(PrintStream printStream) {
this.mPrintStream = printStream;
}
}
class PrimeGenerator {
private static int[] mPrimes;
private static ArrayList<Integer> mMultiplesOfPrimeFactors;
protected static int[] generate(int n) {
mPrimes = new int[n];
mMultiplesOfPrimeFactors = new ArrayList<Integer>();
set2AsFirstPrime();
checkOddNumbersForSubsequentPrimes();
return mPrimes;
}
private static void set2AsFirstPrime() {
mPrimes[0] = 2;
mMultiplesOfPrimeFactors.add(2);
}
private static void checkOddNumbersForSubsequentPrimes() {
int primeIndex = 1;
for (int candidate = 3; primeIndex < mPrimes.length; candidate += 2) {
if (isPrime(candidate))
mPrimes[primeIndex++] = candidate;
}
}
private static boolean isPrime(int candidate) {
if (isLeastRelevantMultipleOfNextLargerPrimeFactor(candidate)) {
mMultiplesOfPrimeFactors.add(candidate);
return false;
}
return isNotMultipleOfAnyPreviousPrimeFactor(candidate);
}
private static boolean isLeastRelevantMultipleOfNextLargerPrimeFactor(int candidate) {
int nextLargerPrimeFactor = mPrimes[mMultiplesOfPrimeFactors.size()];
int leastRelevantMultiple = nextLargerPrimeFactor * nextLargerPrimeFactor;
return candidate == leastRelevantMultiple;
}
private static boolean isNotMultipleOfAnyPreviousPrimeFactor(int candidate) {
for (int n = 1; n < mMultiplesOfPrimeFactors.size(); n++) {
if (isMultipleOfNthPrimeFactor(candidate, n))
return false;
}
return true;
}
private static boolean isMultipleOfNthPrimeFactor(int candidate, int n) {
return candidate == smallestOddNthMultipleNotLessThanCandidate(candidate, n);
}
private static int smallestOddNthMultipleNotLessThanCandidate(int candidate, int n) {
int multiple = mMultiplesOfPrimeFactors.get(n);
while (multiple < candidate)
multiple += 2 * mPrimes[n];
mMultiplesOfPrimeFactors.set(n, multiple);
return multiple;
}
}
重构后的代码新创建了额外的两个类,PrimeGenerator 顾名思义用来生成质数、而RowColumnPagePrinter类用来格式化结果。 每个类中的函数看起来都是那么的一目了然。
也许您会有如下疑问:
重构后的代码执行效率是不是很低啊?从一个函数变为多个类和函数的调用,这些调用操作会不会消耗很多时间啊!
关于这个问题,由于现代的虚拟机优化技术导致函数之间的调用不像远古时代那么耗费时间, 我们只能告诉您这样的代码通常不会引起效率问题,如果有一天您真的发现您代码中有效率问题,需要优化代码,那么是这种结构良好易于阅读的代码更容易优化呢?还是结构糟糕、阅读困得的代码更易优化呢?