minimize

事業拡大のため、新しい仲間を募集しています。
→詳しくはこちら

Javaで保守性の高いプログラムを書くための手法を紹介します。

アクセッサメソッドの必要性

全てのクラスフィールドを public で宣言するなんて事はしてませんよね?
全てのフィールドは private で宣言して、
getter/setterメソッド(アクセッサ)を通してこれらのフィールドにアクセスするのが良い方法です。

しかし、他の言語で開発をしてきた人にとって
何故こんな面倒な事をするのか、疑問に思う人がいるかもしれません。
これこそずばり、保守性の向上を目的としたものなのです。

publicフィールドの例

例を挙げてみます。
日付を表現するクラスを作ったとします。
このクラスは、年を表現するフィールド year を持っています。
このフィールドを public で宣言した場合、フィールドへの代入/参照は
フィールドに直接アクセスすることで行います。

public class SimpleDate {
  public int year;
}
public class Test {
  public void func() {
    SimpleDate obj = new SimpleDate();
    obj.year = 2004;
    System.out.println(obj.year);
  }
}

この方法のメリットは、記述が簡単なことです。
しかし敢えて言わせてもらえば、それだけです。
それによるデメリットの方が圧倒的に大きいのです。

アクセッサメソッドの例

では次に、アクセッサメソッドを用いた例を示します。

public class SimpleDate {
  private int year;
  public int getYear() { return year; }
  public void setYear(int year) { this.year = year; }
}
public class Test {
  public void func() {
    SimpleDate obj = new SimpleDate();
    obj.setYear(2004);
    System.out.println(obj.getYear());
  }
}

少し記述が長くなりました。
一見、こうすることのメリットは特に無いように思えます。
しかし、開発を続けていくうちにその違いがはっきりしてきます。

バグの解明

このクラスを使用したアプリケーションを開発していたところ、
バグが発生してしまいました。
具体的には、日付クラスに負(マイナス)の年数が格納されることが確認されています。

バグを直すには、まずバグの発生場所を調べることが第一です。
しかし、アクセッサメソッドを定義していない場合
この作業は大変手間が掛かってしまいます。
一体どのロジックでこのフィールドへの代入処理を行っているのか判らないからです。

一方、アクセッサメソッドを定義している場合です。
この場合、やるべきことは簡単です。
ソース上からアクセッサメソッドの呼び出し箇所を検索するか、
アクセッサメソッドにブレークポイントを置いてデバッガを走らせるだけです。
そして例えば、アクセッサメソッドを以下のように修正します。

public void setYear(int year) {
  if (year < 0) {
    // エラー発生時の処理を記述
  }
  this.year = year;
}

たった一箇所の修正だけで、問題は全て解決します。

なぜアクセッサメソッドを定義すべきか、判っていただけたでしょうか。
フィールドへの代入/参照ロジックが一箇所に集約されているということが
唯一ですが最大のメリットなのです。
デバッグ/仕様修正。ここに費す時間を軽減させる事こそが、保守性の向上に他なりません。

巌固者の言い訳

これでもまだ、アクセッサメソッドの使用を拒否する人がいるかもしれません。
「記述が面倒だ」「処理が遅くなる」。おそらくこんな理由を付けるのでしょう。
これらはどちらも、使用を拒否する理由にはなり得ません。

前者は、それなりのIDE(もちろんEclipseでも可)を使っていれば問題ありません。
コマンドにより一発でアクセッサメソッドを生成してくれます。

後者は、あなたの明らかな思い込みです。
色々な場所で検証されていますが、
publicフィールドとアクセッサメソッドによる使用を比較した場合
10億回を超えてもその差は計測できる差にはならなかったとあります。
現在のJavaVMは、あなたが思っている以上に限りなく高速化されているのです。

名言があります。「推測するな。計測せよ」
これは非常に大切な事です。いくら人間の脳で推測をしても
その結果は決して実際の結果とは一致しません。
しかし一度計測すれば、その結果は常に実際の結果と一致するのです。

命名規約について

これは、それほど厳密な規約を定めるべきでは無いと考えます。
もちろん、クラス名やpublicメソッド名はある程度の規約に従うべきですが
フィールド名やローカル変数の prefix/suffix は個人に任せるのも一案です。
前述したアクセッサを利用すれば、prefix/suffix は相違は問題では無くなります。

例えば、

private int count;
private int _count;
private int count_;

このいずれの宣言を使ったとしても、getterメソッド名は一貫して getCount です。
簡単にまとめて言えば、
命名規約を厳密に定めるのは public / protected のものだけで良いという事です。

依存性を低くせよ

依存性とは、そのクラスが必要とするクラス(の集合)のことです。
簡単に言えば、このクラスで宣言されているimport文の数に比例します。
なぜ、これが多いことがいけないのでしょうか。

通常、クラスというのは修正されていくものです。
そして当然、その修正の影響というのはそのクラスだけではなく
そのクラスを利用している他のクラスにまで及びます。

依存性が高いという事は、こういった影響を受けやすいという事です。
これは好ましい事ではありません。
一つのクラスを修正したが為に、他の100ものクラスを修正しなければいけないとしたら
それによるバグの混入確率は非常に高いものになってしまうでしょう。

使用するパッケージの数は減らせなくても、依存性を低くする方法があります。
一番判りやすいのがインターフェイスの採用でしょう。
こうやって抽象化しておくことで、実装が変化しても影響を受けないような
作りにすることが可能です。

万能は破滅への道しるべ

何やら危険なタイトルで失礼(笑)。
初心者が陥りがちなのが、何でも機能を詰め込もうとして失敗するパターンです。
設計の段階で「拡張性」などと称して必要の無いメソッドが山ほどあったら、
その設計者は明らかに無能です。

こういった「現在では必要の無いメソッド」というのは
いつになっても未来永劫必要の無いメソッドになり下がるのです。
なぜなら、実際にそのメソッドを使おうとしても
それは余りに抽象的過ぎて(または具体的過ぎて)実際のロジックでは到底使えないものだからです。

こうなった原因は、設計が未熟だったからではありません。
そもそも現在必要の無い機能の詳細を初期の段階で決めることなど不可能なのです。
ここで一言。「必要になってから、その機能を追加しましょう」