TOP > 読み物 > Javaのアンチパターン

Javaのアンチパターン

標準出力(System.out.println)を使わない

標準出力が必要ではない場合、ログ出力API等に置き換えること。
消し忘れが多く、出力元の特定が困難。

アンチパターン
						// 見るだけで殺意を覚えるコード
						System.out.println("DEBUG TEST!!");
						
リファクタリング
						// Log4jで置き換えた例
						if (LOG.isDebugEnabled()) {
							LOG.debug("DEBUG TEST!!");
						}
						

Debug出力は if 文で囲む

ログ出力レベルを「Info」とした際に、「Debug」箇所のコードが評価されてしまう。
性能に影響有り。

アンチパターン
						// 文字列結合部分が評価されてしまう!
						LOG.debug(
							"SampleValue1=" + arg[0]
							+ ", SampleValue2=" + arg[1]
							+ ", SampleValue3=" + arg[2]
						);
						
リファクタリング
						// Log4j DEBUG出力が有効かどうか
						if (LOG.isDebugEnabled()) {
							// たとえ文字列結合でも丁寧に!
							LOG.debug(
								new StringBuilder()
									.append("SampleValue1=").append(arg[0])
									.append(", SampleValue2=").append(arg[1])
									.append(", SampleValue3=").append(arg[2])
									.toString()
							);
						}
						

「値格納」メソッドを使用しない

実際多いが、やりすぎると可読性が悪くなる。
特に設定される対象が、MapとかList、StringBuilderとかになると余計に可読性が悪くなる。

アンチパターン
						// サンプルオブジェクト
						final ExampleObject sampleObj = new ExampleObject();
						// 値を格納している(このメソッド名がややこしい場合は、特にダメ!)
						this.setValues(sampleObj);
						// 値の取得
						System.out.println(sampleObj.getTitle());
						
リファクタリング
						// サンプルオブジェクト
						final ExampleObject sampleObj = this.createExample();
						// 値の取得
						System.out.println(sampleObj.getTitle());
						
						// サンプルオブジェクト
						final ExampleObject sampleObj = new ExampleObject();
						// 値格納メソッドを使用しない
						sampleObj.setTitle(args);
						sampleObj.setText(args);
						// 値の取得
						System.out.println(sampleObj.getTitle());
						

例外から例外を投げない(二重例外)

例外から例外はスローしないようにする。
例外処理に漏れが発生する。

アンチパターン
						// 実装元サンプル
						public class SampleExcetpion extends IOException {
							public SampleExcetpion(String[] args) {
								if (args == null) {
									// 例外の中で例外を投げている
									throw new NullPointerException();
								}
							}
						}
						
						// 呼び元
						public static void main(final String[] args) {
							・・・処理
							// try-catchが漏れていた場合はバグとなる
							throw new SampleExcetpion(args);
						}
						
リファクタリング
						// 実装元サンプル
						public class SampleExcetpion extends IOException {
							public SampleExcetpion(String[] args) {
								// 例外クラス内では例外を発生させない
							}
						}
						
						// 呼び元
						public static void main(final String[] args) {
							if (args == null) {
								// 例外処理をする
							}
							・・・処理
							// Exception生成処理はほとんどが正常終了するべき
							throw new SampleExcetpion(args);
						}
						

共通定義を複数個所に持たせない

共通のシステム定義情報を複数個所に持たせない。
共通定義は、必ず1か所に集約して呼び出させる(メモリの節約にもなる)。
例えば、文字コードやロケール情報といったプロパティ情報。

アンチパターン
						// 例として一番手っ取り早いString
						// 「UTF-8」→「SJIS」に変わった時、死にたくなる
						final String str1 = new String(strBytes, "UTF-8");
						// 他の機能とか
						final String str2 = new String(strBytes, "UTF-8");
						// 他の機能とか
						final String str3 = new String(strBytes, "UTF-8");
						
リファクタリング
						// 実装サンプル
						private static final String DEF_ENCODE = System.getProperty("file.encoding");
						public static String getDefaultEncode() {
							// ・メモリキャッシュした内容を返却するとか
							// ・定義ファイルから取得するとか
							return MySystemConst.DEF_ENCODE;
						}
						
						// 呼び元
						final String str1 = new String(strBytes, MySystemConst.getDefaultEncode());
						// 他の機能とか
						final String str2 = new String(strBytes, MySystemConst.getDefaultEncode());
						// 他の機能とか
						final String str3 = new String(strBytes, MySystemConst.getDefaultEncode());
						

二重チェックイデオムは使用しない(遅延初期化)

二重チェックイデオムは使用しない。
コンストラクタ生成タイミングによってバグとなる(JVM依存)。
回避策として、シングルトン パターンまたはstaticイニシャライザを検討する。
JVMによっては、volatile修飾子でも良い。

アンチパターン
						// JVMによっては、「volatile」修飾子を付ければ動作は保障される
						private static ExampleProperties prop = null;
						// propの読み込み
						public static String getExampleProperties(final String str) {
							// 1回目のチェック
							if (prop == null) {
								// 排他
								synchronized (MySample.class) {
									// 2回目のチェック
									if (prop == null) {
										// prop初期化
										prop = initProperties();
									}
								}
							}
							
							// ほぼ正常に動作するが、
							// 初期化のタイミングでnullが返却される可能性がある(JVM依存)
							return prop;
						}
						
リファクタリング
						// シングルトン パターンの例
						private static final MySample INSTANCE = new MySample();
						private final ExampleProperties prop;
						// シングルトンのコンストラクタで初期化させる
						private MySample() {
							// 初期化
							prop = initProperties();
						}
						
						// propの読み込み
						public static ExampleProperties getExampleProperties() {
							// 返却
							return INSTANCE.prop;
						}
						
						// static イニシャライザの例
						private static ExampleProperties prop = null;
						static {
							// 初期化
							prop = initProperties();
						}
						
						// propの読み込み
						public static ExampleProperties getExampleProperties() {
							// 返却
							return prop;
						}
						
						// 「volatile」修飾子で動作保障がされる
						private static volatile ExampleProperties prop = null;
						// propの読み込み
						public static ExampleProperties getExampleProperties() {
							// 1回目のチェック
							if (prop == null) {
								// 排他
								synchronized (MySample.class) {
									// 2回目のチェック
									if (prop == null) {
										// prop初期化
										prop = initProperties();
									}
								}
							}
							
							// 「volatile」とすることで、一貫性が保たれている。
							return prop;
						}
						

変数の初期化は使用する直前に行う

変数の初期化位置によって対象スコープが決まるため、適切な位置での初期化をする。
ソースの可読性向上とバグ防止。

アンチパターン
						// 初期化位置が悪い(スコープが変)
						final String fileName = "SampleFileName";
						
						if (isCheck()) {
							// 変数 fileName を参照していない・・
						}
						
						・・・処理
						
						// この内部でやっと参照する
						if (isCheckXXX()) {
							// 参照!
							final File file = new File(fileName);
						}
						
リファクタリング
						if (isCheckXXX()) {
							// 正しいスコープ範囲にすることで
							// 可読性やメモリ効率も上がる
							final String fileName = "SampleFileName";
							
							// 参照!
							final File file = new File(fileName);
						}
						

[Swing] 画面更新はEDT上で行う

SwingやAwtの更新が発生する場合は、EDTと呼ばれるイベントディスパッチスレッド上で画面描画を行う。
画面のフリーズやハングアップを引き起こす場合がある。

アンチパターン
						// 画面の表示(これがEDT上で実行されるなら問題ないが・・)
						frame.setVisible(true);
						
リファクタリング
						// SwingUtilitiesを使用して明示的にEDT上で実行させる
						SwingUtilities.invokeLater(new Runnable() {
							@Override
							public void run() {
								frame.setVisible(true);
							}
						});
						
						// 特に時間がかかる場合は、SwingWorkerの実装を検討する
						new SwingWorker<Object, Object>(){

							@Override
							protected Object doInBackground() throws Exception {
								// 時間がかかる処理
								
								// この処理はEDT上では実行されないことに注意
								
								return null;
							}
				
							@Override
							protected void done() {
								// EDT上で実行したい処理
				
								// doInBackgroundで処理した結果を画面に反映する等
								frame.setVisible(true);
							}
				
						}.execute();
						

▲ページの先頭へ