[小西ホームページ]   [目次・索引]   [前の授業]   [次の授業]

コンピュータIIIB(Javaアルゴリズム)第12回

目次
12.1 カプセル化
12.1.1 カプセル化とは
12.1.2 カプセル化の例
12.2 今後の予定
12.3 成績評価について
12.4 演習12
12.5 レポート課題
12.6 参考文献
索引
privateキーワード   カプセル化   情報隠蔽   モジュール  

12.1 カプセル化

12.1.1 カプセル化とは

オブジェクト指向プログラミングにおける重要な概念の一つに、カプセル化があります。 カプセル化encapsulation ) とは、外部からデータの構造が見えないようにし、限られた方法でしかデータにアクセスできないようにすることです。 適切にカプセル化を行いますと、プログラムの作成・変更が容易になります。

カプセル化の本質は情報隠蔽です。 ここで、 情報隠蔽information hiding ) とは、外部に対して必要最小限の情報だけを公開するという考え方です。 公開部分は、外部から利用されますので、簡単には変更できません。 一方、隠蔽部分は、外部から利用されませんので、いつでも変更できます。

関連する情報をひとまとめにしてからカプセル化を行ったプログラム単位は、 モジュールmodule ) と呼ばれます。 プログラムを作成するときに、モジュールを一つ一つ作成するようにしますと、プログラミングがやりやすくなります。

Java言語では、クラスがモジュールの役割を果たします。 つまり、関連する情報は一つのクラスにまとめ、クラスごとにプログラムを作成するということです。 また、情報隠蔽については、クラスのインスタンス変数やインスタンス・メソッドなどが対象となります。 公開するインスタンス変数と隠蔽するインスタンス変数、公開するインスタンス・メソッドと隠蔽するインスタンス・メソッドに分別します。 隠蔽されたインスタンス変数やインスタンス・メソッドを変更しても、他のプログラムは変更しなくてよいのです。

12.1.2 カプセル化の例

カプセル化の例として、何時何分という時刻のクラスを考えます。 クラス名を Time とし、何時を変数 hour , 何分を変数 minute で表すことにします。 メイン・プログラムでは、13時15分というデータを格納してから取り出します。

Time.java(第1版)
/*  1*/ class Time {
/*  2*/     int hour;
/*  3*/     int minute;
/*  4*/ }
TimeMain.java(第1版)
/*  1*/ class TimeMain {
/*  2*/     public static void main (String[] args) {
/*  3*/         Time t = new Time();
/*  4*/         t.hour = 13;
/*  5*/         t.minute = 15;
/*  6*/         System.out.println(t.hour + ":" + t.minute);
/*  7*/     }
/*  8*/ }
b04a001@AisaA1:~/comp3b% java TimeMain
13:15
b04a001@AisaA1:~/comp3b%

もし、変数名 hourminute を、それぞれ jifun に変更しますと、メイン・プログラムも変えなければなりません。

Time.java(第2版)
/*  1*/ class Time {
/*  2*/     int ji;
/*  3*/     int fun;
/*  4*/ }
TimeMain.java(第2版)
/*  1*/ class TimeMain {
/*  2*/     public static void main (String[] args) {
/*  3*/         Time t = new Time();
/*  4*/         t.ji = 13;
/*  5*/         t.fun = 15;
/*  6*/         System.out.println(t.ji + ":" + t.fun);
/*  7*/     }
/*  8*/ }

一か所を変更するとすべてを変更しなければならないようでは、プログラムの変更は困難になってしまいます。

ここでカプセル化を行います。 まず、インスタンス変数は隠蔽します。 次に、インスタンス変数へアクセスするメソッドを用意し、これを公開します。 Java言語では、 private と書きますと、その情報が隠蔽されます。

Time.java(第3版)
/*  1*/ class Time {
/*  2*/     private int hour;
/*  3*/     private int minute;
/*  4*/     void setHour (int hour) {
/*  5*/         this.hour = hour;
/*  6*/     }
/*  7*/     void setMinute (int minute) {
/*  8*/         this.minute = minute;
/*  9*/     }
/* 10*/     int getHour () {
/* 11*/         return this.hour;
/* 12*/     }
/* 13*/     int getMinute () {
/* 14*/         return this.minute;
/* 15*/     }
/* 16*/ }
TimeMain.java(第3版)
/*  1*/ class TimeMain {
/*  2*/     public static void main (String[] args) {
/*  3*/         Time t = new Time();
/*  4*/         t.setHour(13);
/*  5*/         t.setMinute(15);
/*  6*/         System.out.println(t.getHour() + ":" + t.getMinute());
/*  7*/     }
/*  8*/ }

カプセル化を行ったことによって、隠蔽部分であるインスタンス変数を変更しても、メイン・プログラムはそのままでよくなります。

Time.java(第4版)
/*  1*/ class Time {
/*  2*/     private int ji;
/*  3*/     private int fun;
/*  4*/     void setHour (int ji) {
/*  5*/         this.ji = ji;
/*  6*/     }
/*  7*/     void setMinute (int fun) {
/*  8*/         this.fun = fun;
/*  9*/     }
/* 10*/     int getHour () {
/* 11*/         return this.ji;
/* 12*/     }
/* 13*/     int getMinute () {
/* 14*/         return this.fun;
/* 15*/     }
/* 16*/ }
TimeMain.java(第3版のまま)
/*  1*/ class TimeMain {
/*  2*/     public static void main (String[] args) {
/*  3*/         Time t = new Time();
/*  4*/         t.setHour(13);
/*  5*/         t.setMinute(15);
/*  6*/         System.out.println(t.getHour() + ":" + t.getMinute());
/*  7*/     }
/*  8*/ }

カプセル化を行うときは、どの情報を隠蔽し、どの情報を公開するかを適切に決定することが大事です。 特に、インスタンス変数はすべて private にするのが普通です。


12.2 今後の予定

今後の予定は次の通りです。

1月18日(金)
授業中に試験を行います。 今日の演習問題をよく見直しておいてください。
1月25日(金)
この日までに、全員にレポートの提出状況をメールで知らせます。
1月31日(木)
レポートの最終締め切り日です。 これ以降提出されたレポートは採点しません。

12.3 成績評価について

この授業の成績は、レポートの提出と試験の得点で決まります。

S
レポートはすべて提出。 レポートの完成度が高い。 試験の得点は「優」相当。
A
レポートはすべて提出。 試験の得点は「優」相当。
B
レポートはおおむね提出。 試験の得点は「良」相当。
C
レポートは未提出が目立つ。 試験の得点は「可」相当。
F
レポートは未提出が多い。 試験の得点は「不可」相当。

成績に関して次のような事情のある人はメールで連絡してください。 できる限り対処します。


12.4 演習12

問1. 以下のプログラムは、掛け算の九九を計算する(つもりの)ものです。 このプログラムの不具合を修正してください。

/*  1*/ class NineNine {
/*  2*/     public static void main (String[] args) {
/*  3*/         int i, j;
/*  4*/         for (i = 1; i <= 9; i++) {
/*  5*/             for (j = 1; j <= 9; j++) {
/*  6*/                 System.out.print(" " + (i * i));
/*  7*/             }
/*  8*/             System.out.println();
/*  9*/         }
/* 10*/     }
/* 11*/ }
b04a001@AsiaA1:~/comp3b% java NineNine
 1 1 1 1 1 1 1 1 1 // 正しくは 1 2 3 4 5 6 7 8 9
 4 4 4 4 4 4 4 4 4 // 正しくは 2 4 6 8 10 12 14 16 18
 9 9 9 9 9 9 9 9 9 // 正しくは 3 6 9 12 15 18 21 24 27
 16 16 16 16 16 16 16 16 16 // 正しくは 4 8 12 16 20 24 28 32 36
 25 25 25 25 25 25 25 25 25 // 正しくは 5 10 15 20 25 30 35 40 45
 36 36 36 36 36 36 36 36 36 // 正しくは 6 12 18 24 30 36 42 48 54
 49 49 49 49 49 49 49 49 49 // 正しくは 7 14 21 28 35 42 49 56 63
 64 64 64 64 64 64 64 64 64 // 正しくは 8 16 24 32 40 48 56 64 72
 81 81 81 81 81 81 81 81 81 // 正しくは 9 18 27 36 45 54 63 72 81
b04a001@AsiaA1:~/comp3b%

問2. 以下のプログラムは、 f (0) = 0, f ( n ) = 2 * f ( n - 1) + 1( n > 0)で定義される関数を計算する(つもりの)ものです。 この関数は、実際は f ( n ) = 2 ^ n - 1 を計算することが知られています。 このプログラムの不具合を修正してください。

/*  1*/ class Recursive {
/*  2*/     public static void main (String[] args) {
/*  3*/         System.out.println("f(2) = " + f(2));
/*  4*/         System.out.println("f(3) = " + f(3));
/*  5*/         System.out.println("f(4) = " + f(4));
/*  6*/     }
/*  7*/     static int f (int n) {
/*  8*/         if (n == 0) {
/*  9*/             return 0;
/* 10*/         } else {
/* 11*/             return 2 * (n - 1) + 1;
/* 12*/         }
/* 13*/     }
/* 14*/ }
b04a001@AsiaA1:~/comp3b% java Recursive
f(2) = 3 // 正しい
f(3) = 5 // 正しくは 7
f(4) = 7 // 正しくは 15
b04a001@AsiaA1:~/comp3b%

問3. 以下のプログラムは、[1, 2, 3, 4, 5] というリストを構成してから、各要素を2個ずつにする(つもりの)ものです。 このプログラムの不具合を修正してください。

/*  1*/ class ListNode {
/*  2*/     int data;
/*  3*/     ListNode next;
/*  4*/     ListNode (int data, ListNode next) {
/*  5*/         this.data = data;
/*  6*/         this.next = next;
/*  7*/     }
/*  8*/ }
/*  1*/ class ListDouble {
/*  2*/     public static void main (String[] args) {
/*  3*/         int i, DUMMY = 0;
/*  4*/         ListNode x, list = new ListNode(DUMMY, null);
/*  5*/         x = list;
/*  6*/         for (i = 1; i <= 5; i++) {
/*  7*/             x.next = new ListNode(i, null);
/*  8*/         }
/*  9*/         x = list;
/* 10*/         while (x.next != null) {
/* 11*/             x.next = new ListNode(x.data, x.next);
/* 12*/             x = x.next.next;
/* 13*/         }
/* 14*/         for (x = list.next; x != null; x = x.next) {
/* 15*/             System.out.print(" " + x.data);
/* 16*/         }
/* 17*/         System.out.println();
/* 18*/     }
/* 19*/ }
b04a001@AsiaA1:~/comp3b% java ListDouble
 0 5 // 正しくは 1 1 2 2 3 3 4 4 5 5
b04a001@AsiaA1:~/comp3b%

問4. 次のプログラムは、以下の木の葉の個数を数える(つもりの)ものです。 このプログラムの不具合を修正してください。

            10
             |
     +-------+-------+
     |               |
    20              30
     |               |
 +---+---+       +---+
 |       |       |
40      50      60
/*  1*/ class TreeNode {
/*  2*/     int data;
/*  3*/     TreeNode left;
/*  4*/     TreeNode right;
/*  5*/     TreeNode (int data, TreeNode left, TreeNode right) {
/*  6*/         this.data = data;
/*  7*/         this.left = left;
/*  8*/         this.right = right;
/*  9*/     }
/* 10*/ }
/*  1*/ class LeafCount {
/*  2*/     public static void main (String[] args) {
/*  3*/         int DUMMY = 0;
/*  4*/         TreeNode tree = new TreeNode(DUMMY, null, null);
/*  5*/         tree.right = new TreeNode(10, null, null);
/*  6*/         tree.right.left = new TreeNode(20, null, null);
/*  7*/         tree.right.right = new TreeNode(30, null, null);
/*  8*/         tree.right.left.left = new TreeNode(40, null, null);
/*  9*/         tree.right.left.right = new TreeNode(50, null, null);
/* 10*/         tree.right.right.left = new TreeNode(60, null, null);
/* 11*/         System.out.println(leafCount(tree.right));
/* 12*/     }
/* 13*/     static int leafCount (TreeNode tree) {
/* 14*/         if (tree == null) {
/* 15*/             return 1;
/* 16*/         } else if (tree.left == null && tree.right == null) {
/* 17*/             return 0;
/* 18*/         } else {
/* 19*/             return leafCount(tree.left) + leafCount(tree.right);
/* 20*/         }
/* 21*/     }
/* 22*/ }
b04a001@AsiaA1:~/comp3b% java LeafCount
1 // 正しくは 3
b04a001@AsiaA1:~/comp3b%

問5. 次のプログラムは、幅が4, 高さが3の長方形を生成してから、幅も高さも2倍にする(つもりの)ものです。 このプログラムの不具合を修正してください。

/*  1*/ class Rectangle {
/*  2*/     int width = 0;
/*  3*/     int height = 0;
/*  4*/     void setWidth (int width) {
/*  5*/         width = this.width;
/*  6*/     }
/*  7*/     void setHeight (int height) {
/*  8*/         height = this.height;
/*  9*/     }
/* 10*/     int getWidth () {
/* 11*/         return this.width;
/* 12*/     }
/* 13*/     int getHeight () {
/* 14*/         return this.height;
/* 15*/     }
/* 16*/     void expand (int power) {
/* 17*/         power = this.width * power;
/* 18*/         power = this.height * power;
/* 19*/     }
/* 20*/ }
/*  1*/ class RectangleMain {
/*  2*/     public static void main (String[] args) {
/*  3*/         Rectangle rect = new Rectangle();
/*  4*/         rect.setWidth(4);
/*  5*/         rect.setHeight(3);
/*  6*/         rect.expand(2);
/*  7*/         System.out.println(rect.getWidth());
/*  8*/         System.out.println(rect.getHeight());
/*  9*/     }
/* 10*/ }
b04a001@AsiaA1:~/comp3b% java RectangleMain
0 // 正しくは 8
0 // 正しくは 6
b04a001@AsiaA1:~/comp3b%

12.5 レポート課題

今日の演習12の答案(Javaプログラム)をメールで提出してください。 差出人は学内のメール・アドレス(b04a001@cis.twcu.ac.jpなど)とし、宛先はkonishi@cis.twcu.ac.jpとします。 メールの本文には、学生番号、氏名、科目名、授業日(1月11日)を明記してください。


12.6 参考文献


[小西ホームページ]   [目次・索引]   [前の授業]   [次の授業]

2008年1月11日更新
小西 善二郎 <konishi@cis.twcu.ac.jp>
Copyright (C) 2008 Zenjiro Konishi. All rights reserved.