多くの人は愚かな哲学をせず、次のように書きます。
public class FactorialUtil { public static int factorial(int n) { int ret = 1; for (int i = 1; i <= n; ++i) ret *= i; return ret; } }
おそらく思慮深い読者は、「ああ、ホラー、階乗値がintに収まるよりも大きいとしたらどうでしょう?!」と叫び、BigIntegerを使用してコードを書き換える準備ができている、または少なくとも長い:
public class FactorialUtil { public static BigInteger factorial(int n) { BigInteger ret = BigInteger.ONE; for (int i = 1; i <= n; ++i) ret = ret.multiply(BigInteger.valueOf(i)); return ret; } }
毎回1からnまでの中間値を再カウントすることをまだ行っていないことに注意してください。 これらの値をキャッシュすると、一連の操作を保存できます! たとえば、再帰アルゴリズムを使用して、すでに計算された値を保存することができます。
public class FactorialUtil { static HashMap<Integer,BigInteger> cache = new HashMap<Integer,BigInteger>(); public static BigInteger factorial(int n) { BigInteger ret; if (n == 0) return BigInteger.ONE; if (null != (ret = cache.get(n))) return ret; ret = BigInteger.valueOf(n).multiply(factorial(n-1)); cache.put(n, ret); return ret; } }
とても簡単ですね。
明らかに、これらのすべてのメソッドには長所と短所があるため、後でこのライブラリを使用する場合があるため、他の多くのライブラリが使用する手法を使用する必要があります:実行時に計算アルゴリズムを選択できるトリッキーなメカニズム(遅いが経済的メモリ、または高速ですが、多くのスペースが必要です)。 そのため、選択したアルゴリズムへのリンクを持つシングルトンクラスを作成する必要があります。 このクラスは同じインターフェースのままです( 下位互換性のため -およそTransl。)が、アルゴリズム用に別個の(新しく改良された!)エンジンを使用します:
public class FactorialUtil { private static FactorialUtil singleton; private FactorialAlgorithm algorithm; /** * , */ private FactorialUtil() { algorithm = new CachedFactorialImplementation(); } /** * , * @param algorithm */ public FactorialUtil(FactorialAlgorithm a) { algorithm = a; } /** * , , * * @param n * @return */ public static BigInteger factorial(int n) { if (singleton == null) { // singleton = new FactorialUtil(); } return singleton.doFactorial(n); } /** * , * * @param n * @return */ private BigInteger doFactorial(int n) { // return algorithm.factorial(n); } }
このクラスは、シングルトンの作成とアルゴリズムの選択を担当することに注意してください。 古い方法と新しい方法でクラスを使用する機会があります。 アルゴリズムインターフェイスは次のとおりです。
public interface FactorialAlgorithm { BigInteger factorial(int n); }
これは、キャッシングを使用した階乗計算の実装のようになります。
public class CachedFactorialImplementation implements FactorialAlgorithm { static HashMap<Integer,BigInteger> cache = new HashMap<Integer,BigInteger>(); @Override public BigInteger factorial(int n) { BigInteger ret; if (n == 0) return BigInteger.ONE; if (null != (ret = cache.get(n))) return ret; ret = BigInteger.valueOf(n).multiply(factorial(n-1)); cache.put(n, ret); return ret; } }
どれだけシンプルで美しいかを見てください! キャッシュなしで非再帰アルゴリズムを簡単に追加できます。
public class LoopedFactorialImplementation implements FactorialAlgorithm { @Override public BigInteger factorial(int n) { BigInteger ret = BigInteger.ONE; for (int i = 1; i <= n; ++i) ret = ret.multiply(BigInteger.valueOf(i)); return ret; } }
この設計の弱点も明らかです。「オンザフライ」でアルゴリズムを選択することはできません。これは、私たちが計画した重要な機能でした。 したがって、もちろん、アルゴリズムを構成に保存する必要があります。 彼の名前は非常に便利であるため、システムのプロパティに含めることができ、外部ストレージ( XML-およそTransl。など)に簡単に接続できます。 理想的には、主な方法は次のとおりです。
public static void main(String[] args) { System.getProperties().setProperty("com.chaosinmotion.factorialalgorithm", "cachedAlgorithm"); System.out.println("5! = " + FactorialUtil.factorial(5)); }
したがって、アルゴリズムファクトリが必要です。 使用する前にアルゴリズムを作成しないように、シングルトンインスタンス自体とクラスの両方を保持します(実際:なぜ非常に多くのコンストラクターを呼び出して、これが必要になるまで大量のリソースを割り当てるのですか?)
/** * , * @author wwoody * */ public class FactorialAlgorithmFactory { private static HashMap<String,FactorialAlgorithm> mapping = new HashMap<String,FactorialAlgorithm>(); private static HashMap<String,Class<? extends FactorialAlgorithm>> classMapping = new HashMap<String,Class<? extends FactorialAlgorithm>>(); private static FactorialAlgorithm defaultAlgorithm = new CachedFactorialImplementation(); /** */ static { try { Class.forName("com.chaosinmotion.factorial.LoopedFactorialImplementation"); Class.forName("com.chaosinmotion.factorial.CachedFactorialImplementation"); } catch (ClassNotFoundException e) { // } } /** */ public static FactorialAlgorithm getDefaultAlgorithm() { if (defaultAlgorithm == null) { // : , CachedFactorialImplementation // - classpath defaultAlgorithm = getAlgorithm("cachedAlgorithm"); } return defaultAlgorithm; } /** */ public static FactorialAlgorithm getAlgorithm(String name) { FactorialAlgorithm f = mapping.get(name); if (f == null) { // Class<? extends FactorialAlgorithm> c = classMapping.get(name); if (c != null) { // try { f = c.newInstance(); mapping.put(name, f); return f; } catch (Exception e) { // Logger.getLogger("com.chaosinmotion.factorial"). warning("Unable to instantiate algorithm " + c.getCanonicalName() + ", named " + name); } } return getDefaultAlgorithm(); // - } else return f; } /** , */ public static void registerAlgorithm(String name, Class<? extends FactorialAlgorithm> f) { classMapping.put(name, f); } }
次に、名前付きアルゴリズムを使用するようにFactorialUtilクラスを書き直します。
public class FactorialUtil { private static FactorialUtil singleton; private FactorialAlgorithm algorithm; /** * , */ private FactorialUtil() { String name = System.getProperty("com.chaosinmotion.factorialalgorithm", "cachedAlgorithm"); if (name == null) { algorithm = FactorialAlgorithmFactory.getDefaultAlgorithm(); } else { algorithm = FactorialAlgorithmFactory.getAlgorithm(name); } } /** * , * @param algorithm */ public FactorialUtil(FactorialAlgorithm a) { algorithm = a; } /** * . FactorialAlgorithmFactory, * @param name */ public FactorialUtil(String name) { algorithm = FactorialAlgorithmFactory.getAlgorithm(name); } /** * , , * * @param n * @return */ public static BigInteger factorial(int n) { if (singleton == null) { // Use default constructor which uses default algorithm singleton = new FactorialUtil(); } return singleton.doFactorial(n); } /** * , * * @param n * @return */ private BigInteger doFactorial(int n) { // return algorithm.factorial(n); } }
次に、アルゴリズムの実装を更新して、ファクトリーに登録されるようにする必要があります。
public class CachedFactorialImplementation implements FactorialAlgorithm { static HashMap<Integer,BigInteger> cache = new HashMap<Integer,BigInteger>(); static { FactorialAlgorithmFactory.registerAlgorithm("cachedAlgorithm", CachedFactorialImplementation.class); } @Override public BigInteger factorial(int n) { BigInteger ret; if (null != (ret = cache.get(n))) return ret; ret = BigInteger.valueOf(n).multiply(factorial(n-1)); cache.put(n, ret); return ret; } }
そして
public class LoopedFactorialImplementation implements FactorialAlgorithm { static { FactorialAlgorithmFactory.registerAlgorithm("loopedAlgorithm", LoopedFactorialImplementation.class); } @Override public BigInteger factorial(int n) { BigInteger ret = BigInteger.ONE; for (int i = 1; i <= n; ++i) ret = ret.multiply(BigInteger.valueOf(i)); return ret; } }
このアーキテクチャは、新しいアルゴリズムを追加し、それらをFactorialUtilのシングルトンに接続できるという点で美しいです。 FactorialAlgorithmの新しい実装を作成するだけで、ファクトリに登録されます。
public class RecursiveFactorialImplementation implements FactorialAlgorithm { static { FactorialAlgorithmFactory.registerAlgorithm("recursiveAlgorithm", RecursiveFactorialImplementation.class); } @Override public BigInteger factorial(int n) { if (n == 0) return BigInteger.ONE; return BigInteger.valueOf(n).multiply(factorial(n-1)); } }
そして、メインメソッドで、クラスがロードされていることを確認し、システムプロパティを設定します。
public static void main(String[] args) { try { Class.forName("com.chaosinmotion.factorial.RecursiveFactorialImplementation"); } catch (ClassNotFoundException e) { // if this fails, no matter; we'll still use the default implementation. } System.getProperties().setProperty("com.chaosinmotion.factorialalgorithm", "recursiveAlgorithm"); System.out.println("5! = " + FactorialUtil.factorial(5)); }
そして問題ありません! このアーキテクチャを使用すると、たとえば次のようなはるかに複雑なアルゴリズムを接続できます。