前回の自動販売機(Level1)では、まず動くものを作りました。
そこでは1つのメソッド(Main)にすべてをコーディングしていましたが、
これを機能ごとに分割していきます。
実際の仕事では、1つの大きなメソッドにすべてを書くこともありますが、
非常に修正が困難です。
自分にも他人にも優しいプログラミング技術として、「メソッドに小さくわける」
というスキルは身に着けるように意識しておきましょう。
元々のコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
package vm; import java.util.HashMap; import java.util.Map; import java.util.Scanner; public class Main { public static void main(String[] args) { // ①商品の初期化 Map<String, Integer> items = new HashMap<String, Integer>(); items.put("コーラ", 100); items.put("オレンジジュース", 120); items.put("水", 80); // 購入最低金額の場合、追加入金 int deposit = 0; int minSaleAmount = -1; Scanner scanner = new Scanner(System.in); do { // ②入金 System.out.println("お金を入れて下さい。"); deposit = deposit + scanner.nextInt(); // ③金額チェック(最低購入金額) int loopCount = 0; for (String itemKey: items.keySet()) { if(loopCount == 0 || minSaleAmount > items.get(itemKey)) { minSaleAmount = items.get(itemKey); } loopCount++; } } while(deposit < minSaleAmount); // ④商品選択 System.out.println("商品を選択してください。"); Map<String, Integer> availablePurchases = new HashMap<String, Integer>(); for (String itemKey: items.keySet()) { if(deposit >= items.get(itemKey)) { System.out.println(itemKey + ":" + items.get(itemKey) + "円"); availablePurchases.put(itemKey, items.get(itemKey)); } } // ⑤販売 String itemName; while(true) { itemName = scanner.next(); if (availablePurchases.containsKey(itemName)){ break; } System.out.println("商品名の指定が誤っています。商品名を指定し直してください。"); } scanner.close(); System.out.println(itemName + "です!"); // ⑥課金 deposit = deposit - availablePurchases.get(itemName).intValue(); System.out.println("おつりは、" + deposit + "円です。"); } } |
小さいメソッドに分割したコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
package vm_level2; import java.util.HashMap; import java.util.Map; import java.util.Scanner; public class Main { // メインロジック public static void main(String[] args) { // ①商品の初期化 Map<String, Integer> items = initItems(); // 購入最低金額の場合、追加入金 int deposit = 0; Scanner scanner = new Scanner(System.in); do { // ②入金 deposit = deposit(scanner, deposit); // ③金額チェック(最低購入金額) } while (!isMinSaleAmount(items, deposit)); // ④商品選択 Map<String, Integer> availablePurchases = selectItem(items, deposit); // ⑤販売 String itemName = saleItem(scanner, availablePurchases); scanner.close(); // ⑥課金機能 charge(deposit, availablePurchases, itemName); } // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // 以下、メソッド分割 // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< // ①商品の初期化 private static Map<String, Integer> initItems() { Map<String, Integer> items = new HashMap<String, Integer>(); items.put("コーラ", 100); items.put("オレンジジュース", 120); items.put("水", 80); return items; } // ②入金 private static int deposit(Scanner scanner, int deposit) { System.out.println("お金を入れて下さい。"); deposit = deposit + scanner.nextInt(); return deposit; } // ③金額チェック(最低購入金額) private static boolean isMinSaleAmount(Map<String, Integer> items, int deposit) { int minSaleAmount = -1; int loopCount = 0; for (String itemKey : items.keySet()) { if (loopCount == 0 || minSaleAmount > items.get(itemKey)) { minSaleAmount = items.get(itemKey); } loopCount++; } if (deposit < minSaleAmount) { return false; } return true; } // ④商品選択 private static Map<String, Integer> selectItem(Map<String, Integer> items, int deposit) { System.out.println("商品を選択してください。"); Map<String, Integer> availablePurchases = new HashMap<String, Integer>(); for (String itemKey : items.keySet()) { if (deposit >= items.get(itemKey)) { System.out.println(itemKey + ":" + items.get(itemKey) + "円"); availablePurchases.put(itemKey, items.get(itemKey)); } } return availablePurchases; } // ⑤販売 private static String saleItem(Scanner scanner, Map<String, Integer> availablePurchases) { String itemName = ""; while (true) { itemName = scanner.next(); if (availablePurchases.containsKey(itemName)) { break; } System.out.println("商品名の指定が誤っています。商品名を指定し直してください。"); } System.out.println(itemName + "です!"); return itemName; } // ⑥課金 private static void charge(int deposit, Map<String, Integer> availablePurchases, String itemName) { int charge = deposit - availablePurchases.get(itemName).intValue(); System.out.println("おつりは、" + charge+ "円です。"); } } |
説明
①商品の初期化
商品のリストは、Mapで保持しています。
キー:商品名(コーラなど)
値:金額
これをそのまま切り出してメソッド化しました。
これによって、将来商品リストの中身が変わった場合、initItemsメソッドのみを修正すればいいことになります。
②入金
入金した金額を加算する処理の部分だけを切り出しました。
入金額が商品リストの最低金額に達したらという部分は入金処理ではないため、
含まれていません。
③金額チェック(最低購入金額)
ここは入金額が商品リストの最低金額に達したらという判定結果のみを切り出しています。
最低金額商品の取得と、入金額が最低金額商品の金額未満かの判定の2つが機能としてあります。
これはさらに分けてもよさそうな部分です。
④商品選択
入金額以下(つまり、購入可能な商品)のリストを作成している部分を切り出しました。
リストの作成と同時に、System.out.printでも表示を行っているので、機能的には2つあることになります。
ここのさらに分けてよさそうな部分です。
⑤販売
入力された商品名の判定と表示を行う部分を切り出しました。
ここでも、判定と表示はさらに分けてもよい部分ですね。
⑥課金
入金額から商品金額を引いたおつりを算出して表示しています。
よくあるのが、以下のような記述です。
1 |
deposit = deposit - availablePurchases.get(itemName).intValue(); |
次を見て下さい。
1 |
int charge = deposit - availablePurchases.get(itemName).intValue(); |
こちらの方が「入金額から商品金額を引いたらおつりになる」と、直感的に理解できます。
これは、説明用の変数の導入といいます。
ぜひ使えるように意識してください。
参考書籍
最後に
今回はメソッドに分割することを学びました。
メソッドに分解するメリットは、以下の3つです。
・修正箇所が局所化できる。
・処理を共通化して、再利用できるようになる。
・修正の影響範囲を閉じ込めやすい。
修正が面倒だと感じたり、長いプログラムを書くことに違和感を感じたらメソッド化するタイミングです。
ぜひより良いプログラムを書けるようになってください。
Level3
Level3ではクラス分けを行います。
Mainクラスだけではなく、機能を持った別のクラスに分割していきます。
【Javaサンプル】自動販売機(Level3) クラス分け
コメント