オブジェクト指向では、再利用するクラスを作る場合、『カプセル化すべき』と言われています。
これは、
『外部からは、公開されたメソッドを利用することでしか、内部フィールドのデータを操作できないようにすること』
です。
Recoed2クラス(確認後はブラウザの戻る操作で戻ってください)であれば、
Recoed2のフィールド(staticフィールドは除く)のshoとsuuのpublic を 、
privateに変更することで実現できます。
package rec; import java.util.Scanner; import java.io.FileInputStream; import java.io.FileOutputStream; public class Record2 { public static String HIGH_VALUE = "Z99"; // 存在しないデータで、商品コードはこの文字列より小さいコードとする private String sho; // 商品コード private short suu; // 数量 ・・・以下省略・・・
これを施すことにより、
誤ってオブジェクトのデータを変更するようなコードが書き難くなります。
クラス図では、次のように - のつくメンバがprivateの意味になります。
ここで以前に作成したプログラムを、上記のRecord2を使ったものに変更します。
入力データの最小の商品コードを持つレコードを表示するRec2InputMinimum.javaの
プログラムを変更します。
参考⇒(確認後はブラウザの戻る操作で戻ってください)
import rec.Record2; public class Rec2InputMinimum { public static java.util.Scanner stdin = new java.util.Scanner(System.in); public static void main(String[] arg) { Record2 recMin = new Record2(Record2.HIGH_VALUE, 0); //最小レコード憶用 Record2 rec; int n = 0; // 入力データ個数用 int nMin = 0; do { n++; System.out.println(n + "件目入力です"); rec = new Record2(stdin); if (rec.sho.compareTo(recMin.sho) < 0) { recMin = rec;//最小値を更新 nMin = n; } } while (rec.sho.compareTo(Record2.HIGH_VALUE) != 0); System.out.println("入力で、最小(辞書並び)の商品コードのレコードは次の通りです。"); recMin.display(nMin); } }
上記Rec2InputMinimum.javaでは、カプセル化したRecord2クラスを使うようにimportを変更していますが、
このクラスが、このページ冒頭でカプセル化したクラスです。
このカプセル化により、赤の箇所で、アクセスができないエラーが出てしまいます。
D:\Java>javac Rec2InputMinimum.java
Rec2InputMinimum.java:18: sho は rec.Record2 で private アクセスされます。
if (rec.sho.compareTo(recMin.sho) < 0)
^
rec.Record2クラスのshoとsuuがprivateになると、別のクラス(Rec2InputMinimum)では、
内容を見ることも、変更することもできなくなるからです。
つまり、カプセル化したクラスを利用する側は、
直接に内部のフィールドを指定する使い方をしません(できませんが・・)。
逆にカプセル化したクラス側では、使わせるためのメソッドを用意しなければなりません。
Record2クラスの例で、比較の基準が商品コードとなるなら、Record2を引数にして
比較できるメソッドを用意することで、利用側での内部フィールドを指定する必要を無くせます。
次ような compareToメソッドを、Record2クラスに追加するとよいでしょう。
// 商品コードの大小比較 引数の方が大きければ負、小さい時は正、等しいなら0を返します。 public int compareTo(Record2 rec) { return sho.compareTo(rec.sho); }
Rec2InputMinimum.javaを、このメソッドを利用するように変更すれば、 エラー無く実行できるようになります。以下のそのプログラムを示します。 (変更箇所は太字で示します)
import rec.Record2;//カプセル化したRecord2クラスを使う指定 public class Rec2InputMinimum { public static java.util.Scanner stdin = new java.util.Scanner(System.in); public static void main(String[] arg) { Record2 recMin = new Record2(Record2.HIGH_VALUE, 0); //最小レコード憶用 Record2 recEnd = new Record2(Record2.HIGH_VALUE, 0); //終了時入力のレコード Record2 rec; int n = 0; // 入力データ個数用 int nMin = 0; do { n++; System.out.println(n + "件目入力です"); rec = new Record2(stdin); if (rec.compareTo(recMin) < 0) { recMin = rec;//最小値を更新 nMin = n; } } while (rec.compareTo(recEnd) != 0); System.out.println("入力で、最小(辞書並び)の商品コードのレコードは次の通りです。"); recMin.display(nMin); } }
カプセル化を進めることによりオブジェクト内部の仕様変更が外部に影響しなくなります。
しかしその為には、クラスの将来的な利用の使い勝手に応じて、
内部フィールドの値取得や変更用のメソッドを用意しなければなりません。
その場合の操作メソッドのネーミングは、情報の取得にgetXXX 、
情報の設定に、
setXXX(設定用引数) とするのが一般的です。
(XXXは対象のフィールド名です)
数量のsuuのフィールド用であれば、次のメソッドをRecord2クラスに追加すればよいでしょう。
//数量の情報取得メソッド public short getSuu() { return suu; } //数量の情報設定メソッド public void setSuu(int suuData) { suu = (short)suuData; }
以上まで追加変更してきたクラス図を示します
(ソース:Record2.java。)
また数値フィールドであれば、別のオブジェクトの数値フィールドを加算するメソッドがあると
便利な場合があります。
例えばRecoed2のrec1とrec2がある時、
rec1.add( rec2) と使うようなメソッドです。
しかし、可能性のある操作をどんどん追加していくと、ソースが大きくなり、
逆にクラスの目的が読み取り難くなることもあります。
ここでは必要最低限ということで、
これは、追加しないことにします。
rec1の数量にrec2の数量を加算するなら、
次のコードで置き換えできるからです。
rec1.setSuu( rec1.getSuu() + rec2.getSuu() );
なお、shoのフィールドを取得するgetShoメソッドや設定するsetShoメソッドを
追加しないことにしてみます。
作った方が良い可能性もありますが、
shoの商品番号はcompareToメソッドを作ったので、中を見なくても比較ができ、
しかも商品コードが一度決まってしまうと再び変更することは稀だからです。
そこで、作らないでどの程度のことができるか?以降でもこのクラスを使いますので、
必要かどうかは、ご自身で判断ください。