すし注文処理を作成してみよう(Java)

Java

オブジェクト指向の演習問題として、すし注文処理を作成する。
すしデータとして以下のcsvデータを用いる。

にぎり,まぐろ(赤身),100
にぎり,漬けまぐろ,100
にぎり,ビントロ,100
にぎり,真だい,100
にぎり,はまち,100
にぎり,真いわし,100
にぎり,サーモン,100
にぎり,オニオンサーモン,100
にぎり,焼きはらす,100
にぎり,とろサーモン,100
にぎり,そでいか,100
にぎり,大葉甲いか,100
にぎり,大葉真いか,100
にぎり,えんがわにぎり,100
にぎり,えび,100
にぎり,甘えび,100
にぎり,えびアボカド,100
にぎり,生えび,100
にぎり,真たこ,100
にぎり一貫,まぐろ中とろ(一貫),100
にぎり一貫,赤えび(一貫),100
にぎり一貫,うなぎ(一貫),100
にぎり一貫,かににぎり(一貫),100
にぎり一貫,レモンぶり(一貫),100
にぎり一貫,あじ(一貫),100
にぎり一貫,まぐろユッケ手巻き寿司(一貫),100
ぐんかん,ねぎまぐろ,100
ぐんかん,まぐろユッケ,100
ぐんかん,味付いくら,100
ぐんかん,うに,100
ぐんかん,納豆,100
ぐんかん,えびマヨ,100
ぐんかん,サラダ,100
ぐんかん,ツナサラダ,100
ぐんかん,シーフードサラダ,100
ぐんかん,コーン,100
ぐんかん,味玉軍艦,100
ぐんかん,海鮮ねぎ塩軍艦,100
ぐんかん,たらマヨ軍艦,100
ぐんかん,カラフトししゃもっこ軍艦,100
ぐんかん,ほたてうに軍艦,100
ぐんかん,ほたるいか軍艦,100
ぐんかん,ごろっとハム玉軍艦,100
あぶり寿司・細巻き,あぶりサーモンてりマヨ,100
あぶり寿司・細巻き,あぶり豚カルビてりマヨ,100
あぶり寿司・細巻き,あぶりえびチーズ,100
あぶり寿司・細巻き,あぶりチーズサーモン,100
あぶり寿司・細巻き,あぶりチーズ豚カルビ,100
あぶり寿司・細巻き,鉄火巻,100
あぶり寿司・細巻き,きゅうり巻,100
あぶり寿司・細巻き,納豆巻,100
あぶり寿司・細巻き,かんぴょう巻,100
あぶり寿司・細巻き,えび天手巻き寿司(一貫),100
あぶり寿司・細巻き,いか天手巻き寿司(一貫),100
あぶり寿司・細巻き,ぷちずし,100
サイドメニュー,魚介醤油らーめん,360
サイドメニュー,特製茶碗蒸し,180
サイドメニュー,あさり入り赤だし,180
サイドメニュー,京風 だし巻きたまご,100
サイドメニュー,もりもりポテト,230
サイドメニュー,天ぷら盛り合わせ,230
サイドメニュー,いか天,100
サイドメニュー,えび天,100
サイドメニュー,オニオンリング,100
サイドメニュー,京わらびもち,100
サイドメニュー,ミルクレープ,200
サイドメニュー,生ビール,450
サイドメニュー,瓶ビール,500
サイドメニュー,冷酒無添蔵,520
サイドメニュー,オレンジジュース,200

このcsvは
カテゴリー,商品名,価格
で構成されている。以下から同じものをダウンロードできる。

実行例

***ご注文をどうぞ***
***カテゴリ一覧***
0.にぎり
1.にぎり一貫
2.ぐんかん
3.あぶり寿司・細巻き
4.サイドメニュー
番号を入力(e:注文完了)>>0
***にぎり***
0.まぐろ(赤身)(100円)
1.漬けまぐろ(100円)
2.ビントロ(100円)
3.真だい(100円)
4.はまち(100円)
5.真いわし(100円)
6.サーモン(100円)
7.オニオンサーモン(100円)
8.焼きはらす(100円)
9.とろサーモン(100円)
10.そでいか(100円)
11.大葉甲いか(100円)
12.大葉真いか(100円)
13.えんがわにぎり(100円)
14.えび(100円)
15.甘えび(100円)
16.えびアボカド(100円)
17.生えび(100円)
18.真たこ(100円)
番号をカンマ区切りで入力(c:カテゴリ一覧)>>0,0,14
-----注文表-----
まぐろ(赤身)(100円)
まぐろ(赤身)(100円)
えび(100円)
これでよろしいですか(b:戻る,c:カテゴリ一覧,e:注文完了)>>b
***にぎり***
0.まぐろ(赤身)(100円)
1.漬けまぐろ(100円)
2.ビントロ(100円)
3.真だい(100円)
4.はまち(100円)
5.真いわし(100円)
6.サーモン(100円)
7.オニオンサーモン(100円)
8.焼きはらす(100円)
9.とろサーモン(100円)
10.そでいか(100円)
11.大葉甲いか(100円)
12.大葉真いか(100円)
13.えんがわにぎり(100円)
14.えび(100円)
15.甘えび(100円)
16.えびアボカド(100円)
17.生えび(100円)
18.真たこ(100円)
番号をカンマ区切りで入力(c:カテゴリ一覧)>>18
-----注文表-----
まぐろ(赤身)(100円)
まぐろ(赤身)(100円)
えび(100円)
真たこ(100円)
これでよろしいですか(b:戻る,c:カテゴリ一覧,e:注文完了)>>c
***カテゴリ一覧***
0.にぎり
1.にぎり一貫
2.ぐんかん
3.あぶり寿司・細巻き
4.サイドメニュー
番号を入力(e:注文完了)>>2
***ぐんかん***
0.ねぎまぐろ(100円)
1.まぐろユッケ(100円)
2.味付いくら(100円)
3.うに(100円)
4.納豆(100円)
5.えびマヨ(100円)
6.サラダ(100円)
7.ツナサラダ(100円)
8.シーフードサラダ(100円)
9.コーン(100円)
10.味玉軍艦(100円)
11.海鮮ねぎ塩軍艦(100円)
12.たらマヨ軍艦(100円)
13.カラフトししゃもっこ軍艦(100円)
14.ほたてうに軍艦(100円)
15.ほたるいか軍艦(100円)
16.ごろっとハム玉軍艦(100円)
番号をカンマ区切りで入力(c:カテゴリ一覧)>>3,8
-----注文表-----
まぐろ(赤身)(100円)
まぐろ(赤身)(100円)
えび(100円)
真たこ(100円)
うに(100円)
シーフードサラダ(100円)
これでよろしいですか(b:戻る,c:カテゴリ一覧,e:注文完了)>>e
-----注文表-----
まぐろ(赤身)(100円)
まぐろ(赤身)(100円)
えび(100円)
真たこ(100円)
うに(100円)
シーフードサラダ(100円)
合計 600円

仕様

○正常系のみを考慮すればよい(不正な入力はないものとする)
○消費税は考慮しなくてよい
○カテゴリ一覧はcsvデータから動的に生成する

作成

以下のようにSushiApp.javaを作成する

import java.util.*;
import java.io.*;
public class SushiApp{
  public static void main(String[] args) throws Exception{
    
  }
}
class Sushi{
  //フィールド
  String name;
  int price;
  //コンストラクタ
  Sushi(String name,int price){
    this.name=name;
    this.price = price;
  }
  //インスタンスメソッド
  String showInfo(){
    return String.format("%s(%d円)",this.name,this.price);
  }
}

ポイント

○今回はカテゴリをキーとしたMap<String,List<Sushi>>を使ってデータを管理していく
◯Sushiクラスのフィールドはnameとprice
○csvファイルを読み込みながらインスタンスを作成していきたいので引数2つのコンストラクを準備しておく。
○実行例を見ると
まぐろ(赤身)(100円)
という表示がされているので自身の情報を表示するメソッドをインスタンスメソッドとして作成する。

csvファイルを読み込む

さきほどダウンロードしたsushi.csvをカレントディレクトリに配置し、mainメソッドの下に以下のメソッドを記述する。

public class SushiApp{
  public static void main(String[] args){
    
  }
  public static Map<String,List<Sushi>> loadFile(File file){
		Map<String,List<Sushi>> data=new LinkedHashMap<>();
		try (FileInputStream fis = new FileInputStream(file);
				InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
				BufferedReader br = new BufferedReader(isr)) {
			String line;
			while((line = br.readLine())!= null){
				String[] values=line.split(",");
				String cat=values[0];
				String name=values[1];
				int price=Integer.parseInt(values[2]);
				Sushi s = new Sushi(name,price);
				if(!data.containsKey(cat)) {
					data.put(cat,new ArrayList<Sushi>());
				}
				data.get(cat).add(s);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return data;
	}
}

続いて正しく動作するかどうか確認するためにmainメソッドに以下を記述する。

public static void main(String[] args){
    Scanner sc = new Scanner(System.in);
    File file=new File("sushi.csv");
	Map<String,List<Sushi>> data = loadFile(file);
    System.out.println(data.size());
}

実行してみよう。
5
とカテゴリーの件数が表示されればcsvからデータを作成することに成功している。
確認したら、System.outしている1行はコメントアウトしておこう。

Mapの欠点

Mapでデータを作成した場合、keySet()を用いてキーの一覧を得ることはできるが、indexを指定して取り出す手段がない。今回はカテゴリーをindex表示する必要があるのでkeySetを元にカテゴリーの入ったList<String>を作成しよう。以下のように追記する

public static void main(String[] args){
    Scanner sc = new Scanner(System.in);
    File file=new File("sushi.csv");
	Map<String,List<Sushi>> data = loadFile(file);
    //System.out.println(data.size());
	//カテゴリーリストの作成
    List<String> catList=new ArrayList<>(data.keySet());
}

showCategoryメソッドの作成

loadFileメソッドの下に以下のようなカテゴリーを一覧するメソッドを作成する

	public static void showCategory(List<String> catList) {
		System.out.println("***カテゴリ一覧***");
		for(int i=0;i<catList.size();i++) {
			System.out.printf("%d.%s%n", i,catList.get(i));
		}
	}

実行してみよう。bの戻るやcのカテゴリ一覧へうまく遷移できていることがわかる。問題はeを選択したときだ。現状の骨組みを以下に示す。

public static void main(String[] args) throws Exception{
    略 
    System.out.println("***ご注文をどうぞ***");
    while(true){
      ~カテゴリ選択部分~
      Category selectedCategory = categoryList.get(no);
      while(true){
        ~カテゴリ内商品一覧~
        displayOrderSheet(orderedSushiItems,false); 
        System.out.print("これでよろしいですか(b:戻る,c:カテゴリ一覧,e:注文完了)>>");
        input=sc.next();
        if(input.equalsIgnoreCase("b")){
          //カテゴリ内商品一覧へ飛ぶ
          continue;
        }
        if(input.equalsIgnoreCase("c")){
          //カテゴリ選択へ飛ぶ
          break;
        }
        if(input.equalsIgnoreCase("e")){
          //最初のwhile(true)を抜ける

        }
      }
    }
  }

eを押したときは最初のwhileループを抜けたい。この場合はどうすればよいだろうか?

方法1:ラベル付きbreak

こういった時にはラベル付きbreakが使える。

public static void main(String[] args) throws Exception{
    略 
    System.out.println("***ご注文をどうぞ***");
    outer:while(true){
      ~カテゴリ選択部分~
      Category selectedCategory = categoryList.get(no);
      while(true){
        ~カテゴリ内商品一覧~
        displayOrderSheet(orderedSushiItems,false); 
        System.out.print("これでよろしいですか(b:戻る,c:カテゴリ一覧,e:注文完了)>>");
        input=sc.next();
        if(input.equalsIgnoreCase("b")){
          //カテゴリ内商品一覧へ飛ぶ
          continue;
        }
        if(input.equalsIgnoreCase("c")){
          //カテゴリ選択へ飛ぶ
          break;
        }
        if(input.equalsIgnoreCase("e")){
          //最初のwhile(true)を抜ける
          break outer;
        }
      }
    }
  }

抜けたいループにラベルを付けて、breakする際にラベルを指定する。
ただ、この方法は少しマニアックだし、こういったgoto 文のようなジャンプ処理をやりだすとスパッゲティコードと呼ばれる処理の順序を追いにくい状態になってしまうため嫌う人もいる。(個人的にはこの程度なら全く問題ないとは思うが。。。)

方法2:フラグを立てる

こういった場合の一般解はフラグを立てるという方法だ。

  public static void main(String[] args) throws Exception{
    略 
    System.out.println("***ご注文をどうぞ***");
    boolean onOrder=true;
    while(onOrder){
      ~カテゴリ選択部分~
      Category selectedCategory = categoryList.get(no);
      while(true){
        ~カテゴリ内商品一覧~
        displayOrderSheet(orderedSushiItems,false); 
        System.out.print("これでよろしいですか(b:戻る,c:カテゴリ一覧,e:注文完了)>>");
        input=sc.next();
        if(input.equalsIgnoreCase("b")){
          //カテゴリ内商品一覧へ飛ぶ
          continue;
        }
        if(input.equalsIgnoreCase("c")){
          //カテゴリ選択へ飛ぶ
          break;
        }
        if(input.equalsIgnoreCase("e")){
          //最初のwhile(true)を抜ける
          onOrder=false;
          break;
        }
      }
    }
  }

booleanの変数を設定して、このような処理を実現していく方法をフラグを立てる
という。フラグというのflag(旗)のことで旗を立てたり下ろしたりして処理を制御していくとことでとてもよく用いられる。今回は注文中ということでonOrderというboolean変数を用意した。

最後にwhileを抜けた(注文が完了した)ときの処理をwhileループの下に追記しよう。

public static void main(String[] args) throws Exception{
    略 
    System.out.println("***ご注文をどうぞ***");
    boolean onOrder=true;
    while(onOrder){
      ~カテゴリ選択部分~
      Category selectedCategory = categoryList.get(no);
      while(true){
        ~カテゴリ内商品一覧~
        displayOrderSheet(orderedSushiItems,false); 
        System.out.print("これでよろしいですか(b:戻る,c:カテゴリ一覧,e:注文完了)>>");
        input=sc.next();
        if(input.equalsIgnoreCase("b")){
          //カテゴリ内商品一覧へ飛ぶ
          continue;
        }
        if(input.equalsIgnoreCase("c")){
          //カテゴリ選択へ飛ぶ
          break;
        }
        if(input.equalsIgnoreCase("e")){
          //最初のwhile(true)を抜ける
          onOrder=false;
          break;
        }
      }
    }
    displayOrderSheet(orderedSushiItems,true);
  }

完成

以上で完成だ。実行例のようになるか確認してみよう。

ソースコード全文

import java.util.*;
import java.io.*;
public class SushiApp{
  public static void main(String[] args) throws Exception{
    Scanner sc =new Scanner(System.in);
    File file  = new File("sushi.csv");
    ArrayList<Sushi> allData = loadFile(file);
    ArrayList<Category> categoryList= createCategoryList(allData);
    
    ArrayList<Sushi> orderedSushiItems = new ArrayList<>();
    System.out.println("***ご注文をどうぞ***");
    boolean onOrder=true;
    while(onOrder){
      displayCategoryList(categoryList);
      System.out.print("番号を入力(e:注文完了)>>");
      String input = sc.next();
      if(input.equalsIgnoreCase("e")){
        break;
      }
      int no = Integer.parseInt(input);
      Category selectedCategory = categoryList.get(no);
      while(true){
        selectedCategory.showLabel();
        selectedCategory.showCategoryItems();
        System.out.print("番号をカンマ区切りで入力(c:カテゴリ一覧)>>");
        input = sc.next();
        if(input.equalsIgnoreCase("c")){
          break;
        }
        String[] nums = input.split(",");
        for(String n : nums){
          int idx = Integer.parseInt(n);
          Sushi sushi = selectedCategory.getItem(idx);
          orderedSushiItems.add(sushi);
        }
        displayOrderSheet(orderedSushiItems,false); 
        System.out.print("これでよろしいですか(b:戻る,c:カテゴリ一覧,e:注文完了)>>");
        input=sc.next();
        if(input.equalsIgnoreCase("b")){
          continue;
        }
        if(input.equalsIgnoreCase("c")){
          break;
        }
        if(input.equalsIgnoreCase("e")){
          onOrder = false;
          break;
        }
      }
    }
    displayOrderSheet(orderedSushiItems,true);
  }

  static void displayOrderSheet(ArrayList<Sushi> orderedSushiItems,boolean total){
    System.out.println("-----注文表-----");
    int sum =0;//合計金額を保持する変数
    for(Sushi sushi : orderedSushiItems){
      sum += sushi.price;
      System.out.println(sushi.showInfo());
    }
    if(total){
      System.out.printf("合計 %d円%n",sum);
    }
  }
  static void displayCategoryList(ArrayList<Category> cats){
    System.out.println("***カテゴリ一覧***");
    for(int i=0;i<cats.size();i++){
      System.out.printf("%d.%s%n",i,cats.get(i).catName);
    }
  }
  static ArrayList<Sushi> loadFile(File file) throws Exception{
    ArrayList<Sushi> list =new ArrayList<>();
    FileInputStream fis=new FileInputStream(file);
    InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
    BufferedReader br = new BufferedReader(isr);
    String line;
    while((line = br.readLine())!=null){
      String[] values=line.split(",");
      String category=values[0];
      String name=values[1];
      int price = Integer.parseInt(values[2]);

      Sushi s = new Sushi(category,name,price);
      list.add(s);
    }
    br.close();
    return list;
  }
  static ArrayList<Category> createCategoryList(ArrayList<Sushi> allData){
    ArrayList<String> catNames = new ArrayList<>();
    for(Sushi sushi : allData){
      if(!catNames.contains(sushi.category)){
        catNames.add(sushi.category);
      }
    }
    ArrayList<Category> categoryList =new ArrayList<>();
    for(String catName:catNames){
      Category c=new Category(catName);
      for(Sushi sushi :allData){
        if(sushi.category.equals(catName)){
          c.addItem(sushi);
        }
      }
      categoryList.add(c);
    }
    return categoryList;
  }
}
class Sushi{
  String category;
  String name;
  int price;
  Sushi(String category,String name,int price){
    this.category = category;
    this.name=name;
    this.price = price;
  }
  String showInfo(){
    return String.format("%s(%d円)",this.name,this.price);
  }
}
class Category{
  String catName;//カテゴリ名
  ArrayList<Sushi> sushiList=new ArrayList<>();//このカテゴリのSushiが入るリスト
  Category(String catName){
    this.catName=catName;
  }
  void showLabel(){
    System.out.println("***"+this.catName+"***");
  }
  void addItem(Sushi sushi){
    this.sushiList.add(sushi);
  }
  Sushi getItem(int index){
    return this.sushiList.get(index);
  }
  void showCategoryItems(){
    for(int i=0;i<this.sushiList.size();i++){
      System.out.printf("%d.%s%n",i,this.sushiList.get(i).showInfo());
    }
  }
}
Java
スポンサーリンク
シェアする
mjpurinをフォローする

コメント

タイトルとURLをコピーしました