~手続き指向からオブジェクト指向へ[Ⅱ]~ 第6回 データ構造とアルゴリズムの結合 ~手続き指向からオブジェクト指向へ[Ⅱ]~
学習目標 データ構造とアルゴリズムを結合させたクラスを使ったプログラムが書ける データ構造とアルゴリズムが結合することの利点を説明できる クラスのデータをカプセル化することができる データをカプセル化することの利点を説明できる クラス図が読める
6.1 アルゴリズムと データ構造の結合 6.1.1 仕様変更に伴うMainプログラムの変更 6.1.2 問題の本質 6.1.3 データ構造とアルゴリズムの結合 6.1.4 データ構造とアルゴリズムを結合したプログラムを書く
6.1.1 仕様変更 ①配列リストによる商品種類プログラム ②実装方法変更 ③またまた実装変更!?
①配列リスト②連結リスト ①第4回で実装した配列版 ItemTypeListクラス 今回はItemTypeArrayListクラスとします ②第5回で実装した連結リスト版ItemTypeListクラス 今回はItemTypeLinkedListクラスとします
③戻したら動かなくなった どこがまずいのか 考えてみよう 例題6-3 (Example6_3.java) //例題6-3:また仕様変更 public class Example6_3 { //取り扱う商品種類を追加するプログラム public static void main(String[] args) { //商品種類配列リストを生成する ItemTypeArrayList itemTypeList = new ItemTypeArrayList(); //商品種類を保存するための配列を定義する ItemType[] itemTypeArray = new ItemType[100]; //商品種類を追加する for(int i=0;i<100;i++){ itemTypeList.add(itemTypeArray,new ItemType(i,"コーラ",120)); } //商品種類リストを表示する itemTypeList.display(itemTypeArray); 例題6-3 (Example6_3.java) どこがまずいのか 考えてみよう
実はItemTypeListが原因 /** * 商品種類を追加する */ 例題4-5 (Example4_5.java) /** * 商品種類を追加する */ public void add(ItemType[] targetArray,ItemType addItemType){ //商品種類が入っていない箱を探す for(int i=0;i<10;i++){ if(targetArray[i] == null){//入っていない targetArray[i] = addItemType;//書き込む break; }
6.1.2 問題の本質 ケアレスミスを防ぐことはできない ケアレスミスをした時に原因を発見できるプログラムを書くほうが重要 人間が書くものだから ケアレスミスをした時に原因を発見できるプログラムを書くほうが重要 他人に分かるように書く クラスは意味のカタマリに プログラムは意味を明確に 問題の本質を探ってみよう ①クラスの役割分担から ②意味が明確なプログラムという面から
①クラスの役割分担 Exampleにバグがあると思ったら、ItemTypeListに原因があった 役割分担がうまくいってない
①クラスの役割分担 配列版 配列操作 アルゴリズムを 持ってる 配列を持ってる [5] [4] [3] [2] [1] [0] -1 Example ItemTypeList add() insert() 連結リスト版 連結リストを 持ってる 連結リスト操作 アルゴリズムを 持ってる 102番地 コーラ 22(id) null ソーダ 23(id) Example ItemTypeList add() insert()
②意味が明確なプログラム 現状のプログラムは、データ構造(配列)を気にしすぎる 現在のプログラム 「配列」に 「商品種類」を追加する より意味が明確なプログラムのほうが、意図した動作を期待しやすい //メインクラス public class Example6_3 { //取り扱う商品種類を追加するプログラム public static void main(String[] args) { //商品種類配列リストを生成する ItemTypeArrayList itemTypeList = new ItemTypeArrayList(); //商品種類を保存するための配列を定義する ItemType[] itemTypeArray = new ItemType[100]; //商品種類を追加する for(int i=0;i<100;i++){ itemTypeList.add(itemTypeArray,new ItemType(i,"コーラ",120)); } //商品種類リストを表示する itemTypeList.display(itemTypeArray); 現在のプログラム 「配列」に 「商品種類」を追加する 本来の意味(目的) 「商品種類リスト」に 「商品種類」を追加する
②意味が明確なプログラム 配列操作 アルゴリズム [5] [4] [3] [2] [1] [0] -1 add() insert() ItemTypeArrayList Example なにを やりたいんだっけ? 連結リスト操作 アルゴリズム ItemTypeLinkedList 102番地 コーラ 22(id) null ソーダ 23(id) add() insert()
6.1.3 データ構造と アルゴリズムの結合 2つ合わせて初めて意味がある 配列を使うには -1 -1 -1 -1 -1 -1 [5] 配列用アルゴリズム add() delete() search() display() -1 -1 -1 -1 -1 -1 [5] [4] [3] [2] [1] [0] 連結リストを使うには 連結リスト用アルゴリズム add() delete() search() display() 102番地 コーラ 22(id) null ソーダ 23(id)
6.1.3 アルゴリズム とデータ構造の結合 データ構造とアルゴリズムを結合して 「意味のカタマリ」を作る 2つ合わせて初めて「リスト」という意味がある 配列操作 アルゴリズム [5] [4] [3] [2] [1] [0] -1 add() insert() ItemTypeArrayList
Javaでのプログラム(配列) データ構造 と それを操作する アルゴリズム 例題6-4 (Example6_4.java) //商品種類リスト配列実装クラス public class ItemTypeArrayList { public ItemType[] itemTypeArray;//商品種類を保存する配列 //商品種類を追加するメソッド public void add(ItemType addItemType){ //商品種類が入っていない箱を探す for(int i=0;i<10;i++){ if(itemTypeArray[i] == null){//入っていない itemTypeArray[i] = addItemType;//書き込む break; } データ構造 アルゴリズム と それを操作する
Javaでのプログラム(連結リスト) データ構造 と それを操作する アルゴリズム 例題6-5 (Example6_5.java) //商品種類リスト連結リスト実装クラス public class ItemTypeLinkedList { public LinkObject first;//連結リスト始点 public LinkObject last;//連結リスト終点 //商品種類を追加するメソッド public void add(ItemType addItemType){ LinkObject addLink = new LinkObject(); addLink.data = addItemType; if(first == null){//連結リストが空のとき first = addLink; last = addLink; }else{//連結リストが空でないとき last.next = addLink; } データ構造 アルゴリズム と それを操作する
データ構造と アルゴリズムを結合する利点 議論してみましょう
①うまい役割分担 役割分担を考え、各オブジェクトが責任を持ち、うまくコミュニケーションできるようにするのがオブジェクト指向設計の基本です 商品種類の管理役 命令役 配列操作 アルゴリズム [5] [4] [3] [2] [1] [0] -1 Example add() insert() ItemTypeArrayList
②意味が明確なプログラム より意味が明確な プログラムに 例題6-4 (Example6_4.java) Main() //商品種類配列リストを生成する ItemTypeArrayList itemTypeList = new ItemTypeArrayList(); //商品種類を保存するための配列を定義する ItemType[] itemTypeArray = new ItemType[100]; //商品種類を追加する itemTypeList.add(itemTypeArray,new ItemType(1001,"コーラ",120)); //商品種類リストを表示する itemTypeList.display(itemTypeArray); 例題6-4 (Example6_4.java) Main() //商品種類リストオブジェクトをインスタンス化する ItemTypeArrayList itemTypeArrayList = new ItemTypeArrayList(); //商品種類を追加する itemTypeArrayList.add(new ItemType(1001,"コーラ")); //商品種類リストを表示する itemTypeArrayList.display(); より意味が明確な プログラムに
②意味が明確なプログラム 配列操作 アルゴリズム [5] [4] [3] [2] [1] [0] -1 add() insert() ItemTypeArrayList 1001 追加 Example ItemTypeLinkedList 連結リスト操作 アルゴリズム 1001 追加 add() insert() 102番地 コーラ 22(id) null ソーダ 23(id)
②意味が明確なプログラム 「商品種類」を「リスト」に追加するという意味の明確なプログラムが書ける 配列操作 アルゴリズム [5] [4] [3] [2] [1] [0] -1 add() insert() Example 1001 ItemTypeArrayList 追加
バグの発見が容易 人間は意味のカタマリに注目して、物事を考える 責任分担をして、「意味のカタマリ」を作り、意味の明確なプログラムを書くことが重要、可読性も上がる ItemTypeArrayList Example 1001 追加 ?? オマエが悪い!
6.2 カプセル化 6.2.1 不正な操作 6.2.2 不正な操作を防ぐ 6.2.3 Javaにおけるカプセル化 6.2.4 商品種類クラスのカプセル化
6.2.1 不正な操作 以下の例は、誤ったItemTypeArrayListの使い方です これでもプログラムは動いてしまいます! //商品種類リストオブジェクトをインスタンス化する ItemTypeArrayList itemTypeArrayList = new ItemTypeArrayList(); //商品種類を追加する itemTypeArrayList.add(new ItemType(1001,"コーラ")); itemTypeArrayList.add(new ItemType(1002,"ソーダ")); itemTypeArrayList.itemTypeArray[0] = null; これでもプログラムは動いてしまいます!
不正な ItemTypeArrayListの使い方 正規の操作 [5] [4] [3] [2] [1] [0] -1 Example ItemTypeArrayList 1001 追加 add() insert() 1055 [5] [4] [3] [2] [1] [0] -1 不正な操作 Example ItemTypeArrayList add() insert()
不正はバグの原因 ItemTypeArrayListは、直接配列をいじられるようには作られていません 間にnull等が入ると、おかしな動作をしてしまいます 意味が明確なaddメソッドを利用して欲しい
6.2.2 不正な操作を防ぐ データの「カプセル化」をして、オブジェクトの外から参照できないようにします 参照できるのはメソッドだけ ItemTypeArrayList add() insert() [5] [4] [3] [2] [1] [0] -1
6.2.3 Javaにおけるカプセル化 「public」だったアクセス修飾子を「private」に変えます //商品種類リスト配列実装クラス public class ItemTypeArrayList { private ItemType[] itemTypeArray;//商品種類を保存する配列 //商品種類を追加するメソッド public void add(ItemType addItemType){ //商品種類が入っていない箱を探す for(int i=0;i<10;i++){ if(itemTypeArray[i] == null){//入っていない itemTypeArray[i] = addItemType;//書き込む break; }
不正できなくなる アクセス修飾子「private」がついていれば、 コンパイルエラーとなります //商品種類リストオブジェクトをインスタンス化する ItemTypeArrayList itemTypeArrayList = new ItemTypeArrayList(); //商品種類を追加する itemTypeArrayList.add(new ItemType(1001,"コーラ")); itemTypeArrayList.add(new ItemType(1002,"ソーダ")); itemTypeArrayList.itemTypeArray[0] = null; アクセス修飾子「private」がついていれば、 コンパイルエラーとなります
①Javaで使えるアクセス修飾子 public private protected 何もつけない すべてのクラスから参照可能 そのクラス内でのみ参照可能 protected そのクラスとそのサブクラスでのみ参照可能 何もつけない パッケージ内でのみ参照可能
アクセス修飾子を 理解できたか、簡単なテスト /** * アクセス修飾子を解説するためのクラス */ public class Access{ public int dataA; private int dataB; private int getDataA(){ return dataA; } public int getDataB(){ return dataB;
答え
6.2.4 商品種類クラスのカプセル化 現状のItemTypeクラス //商品種類のクラス public class ItemType{ 6.2.4 商品種類クラスのカプセル化 現状のItemTypeクラス //商品種類のクラス public class ItemType{ int id; // 商品番号 String name; // 商品名 //コンストラクタ public ItemType(int initId, String initName){ id = initId; name = initName; }
保守性を高くした ItemTypeクラス 商品番号と商品名への 直な参照を禁止します。 変わりに、publicで それらを得ることができる 例題6-6 (Example6_6.java) //商品種類のクラス public class ItemType{ private int id; // 商品番号 private String name; // 商品名 //コンストラクタ public ItemType(int initId, String initName){ id = initId; name = initName; } //商品番号を得るメソッド public int getId(){ return id; //名前を得るメソッド public String getName(){ return name; 商品番号と商品名への 直な参照を禁止します。 変わりに、publicで それらを得ることができる メソッドを用意します。 これで、商品番号と商品名はコンストラクタで 一旦設定されたら、外部から変更できなくなりました。
外部から設定変更を許すとき 変更を許す属性にのみ publicな変更メソッドを 用意します //商品種類のクラス public class ItemType{ private int id; // 商品番号 private String name; // 商品名 //コンストラクタ省略 //商品番号を得るメソッド public int getId(){ return id; } //名前を得るメソッド public String getName(){ return name; //名前を設定するメソッド public void setName(String newName){ name = newName; 変更を許す属性にのみ publicな変更メソッドを 用意します
何故設定と取得を分けるの? VS 利用する時を 考えよう itemType.name = "コーラ"; //商品種類 public class ItemType{ private String name; // 商品名 //名前を得る public String getName(){ return name; } //名前を設定する public void setName(String newName){ name = newName; //商品種類のクラス public class ItemType{ public String name; // 商品名 } VS itemType.name = "コーラ"; System.out(itemType.name) itemType.setName("コーラ") System.out(itemType.getName()) 利用する時を 考えよう
利用するクラスから見れば ItemTypeListの変更 コンパイルエラー 取得?設定? 意味が明らか System.out.println(itemTypeArray[i].name+"は販売中です"); 意味が明らか System.out.println(itemTypeArray[i].getName()+"は販売中です");
設定と取得を分ける理由 クラスを利用する側から見れば クラスから見れば、 取得なのか設定なのか明確なプログラムが書ける 取得はできるけど設定はできない変数 設定されたら困る変数 取得した時だけログをとりたい場合 設定した時だけログをとりたい場合
6.3 クラスの構造を図解する 6.3.1 クラス図とは 6.3.2 記法 6.3.3 今回までのクラス図 ①クラスを表現する ②クラス間の関係を表現する 6.3.3 今回までのクラス図
6.3.1 クラス図とは クラス図 Example,ItemType,ItemTypeListなど、クラスの構成が複雑化してきました 見やすいように図解する方法 クラス図
クラス図 クラス図はクラス構造を分かりやすくするためのものです
記法①クラス クラス名 変数 可視性 public+ private - メソッド
記法②関係 「関係」は、Javaで参照を持っていることを意味し、線で結びます ListクラスはItemTypeを変数(配列)で参照しているので関係があります
多重度 1対nの関係 単なる関係だけではインスタンスを何個参照しているのか分からないので、多重度をつかいます クラス オブジェクト 1001 コーラ オブジェクト itemTypeArrayList 1002 ソーダ 1003 お茶
今回までのクラス図