LoginSignup
2
4

More than 3 years have passed since last update.

【改善編】簡単なツール作成を通してJavaを学ぶ

Last updated at Posted at 2019-09-17

お題

前回、Golangで書いた簡易ツール「ローカルJSONファイルに保存するキーバリューストア」のJava版。
シリーズ物としては、もともと複数言語で同じ内容のプログラムを書いて比較した「簡単なツール作成を通して各プログラミング言語を比較しつつ学ぶ」からの改善版。

試行Index

実装・動作確認端末

# 言語バージョン

$ java -version
openjdk version "1.8.0_202"
OpenJDK Runtime Environment (build 1.8.0_202-20190206132807.buildslave.jdk8u-src-tar--b08)
OpenJDK GraalVM CE 1.0.0-rc14 (build 25.202-b08-jvmci-0.56, mixed mode)

# IDE - IntelliJ IDEA

IntelliJ IDEA 2019.2 (Ultimate Edition)
Build #IU-192.5728.98, built on July 23, 2019

実践

要件

アプリを起動すると、キーバリュー形式でテキスト情報をJSONファイルに保存する機能を持つコンソールアプリ。
オンメモリで保持していた点だけ除けば第1回と同じ仕様なので詳細は以下参照。
https://qiita.com/sky0621/items/32c87aed41cb1c3c67ff#要件

ソース全量

解説

全ソースファイル

Javaソース 説明
Main.java アプリ起動エントリーポイント
StoreInfo.java キーバリュー情報を保存するストア(JSONファイル)に関する情報を扱う。
現状は「ファイル名」だけ保持
Commands.java キーバリューストアからの情報取得や保存、削除といった各コマンドを管理。
コマンドの増減に関する影響は、このソースに閉じる。
Command.java 各コマンドに共通のインタフェース
SaveCommand.java キーバリュー情報の保存を担う。
GetCommand.java 指定キーに対するバリューの取得を担う。
ListCommand.java 全キーバリュー情報の取得を担う。
RemoveCommand.java 指定キーに対するバリューの削除を担う。
ClearCommand.java 全キーバリュー情報の削除を担う。
HelpCommand.java ヘルプ情報の表示を担う。
EndCommand.java アプリの終了を担う。

[Main.java]アプリ起動エントリーポイント

[Main.java]
import java.util.Scanner;

public class Main {
    public static void main(String... args) {
        Commands commands = new Commands(new StoreInfo("store.json"));

        System.out.println("Start!");
        while (true) {
            Scanner s = new Scanner(System.in);
            String cmd = s.nextLine();
            commands.exec(cmd.split(" "));
        }
    }
}

[StoreInfo.java]ストア情報の管理

[StoreInfo.java]
import org.apache.commons.lang3.StringUtils;

public class StoreInfo {
    private static final String DEFAULT_STORE_NAME = "store.json";

    private String storeName;

    public StoreInfo(String storeName) {
        if (StringUtils.isEmpty(storeName)) {
            this.storeName = DEFAULT_STORE_NAME;
        } else {
            this.storeName = storeName;
        }
    }

    public String getName() {
        return this.storeName;
    }
}

[Commands.java]各コマンドの管理

[Commands.java]
import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Commands {
    private Map<String, Command> commands;

    public Commands(StoreInfo storeInfo) {
        commands = new HashMap<>();
        commands.put("end", new EndCommand());
        commands.put("help", new HelpCommand());
        commands.put("clear", new ClearCommand(storeInfo));
        commands.put("save", new SaveCommand(storeInfo));
        commands.put("get", new GetCommand(storeInfo));
        commands.put("remove", new RemoveCommand(storeInfo));
        commands.put("list", new ListCommand(storeInfo));

        if (!new File(storeInfo.getName()).exists()) {
            commands.get("clear").exec(null);
        }
    }

    public void exec(String[] cmds) {
        if (cmds == null || cmds.length < 1) {
            System.out.println("no target");
            return;
        }

        List<String> cmdList = Arrays.asList(cmds);
        String[] args = cmdList.subList(1, cmdList.size()).toArray(new String[0]);
        this.commands.get(cmds[0]).exec(args);
    }
}

[Command.java]各コマンドの親クラス

[Command.java]
public interface Command {
    void exec(String[] args);
}

各コマンドクラス

各コマンドについては、ストア情報がオンメモリのハッシュからJSONに変わった点以外はやることは一緒。(なので説明省く。)

■保存

[SaveCommand.java]
import com.google.gson.Gson;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class SaveCommand implements Command {
    private StoreInfo storeInfo;

    public SaveCommand(StoreInfo storeInfo) {
        this.storeInfo = storeInfo;
    }

    @Override
    public void exec(String[] args) {
        if (args == null || args.length != 2) {
            System.out.println("not valid");
            return;
        }

        Gson gson = new Gson();
        Path p = Paths.get(this.storeInfo.getName());
        try {
            Map<String, String> now = gson.fromJson(Files.readAllLines(p).get(0), HashMap.class);
            now.put(args[0], args[1]);
            Files.write(p, gson.toJson(now).getBytes());
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
}

■1件取得

[GetCommand.java]
import com.google.gson.Gson;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class GetCommand implements Command {
    private StoreInfo storeInfo;

    public GetCommand(StoreInfo storeInfo) {
        this.storeInfo = storeInfo;
    }

    @Override
    public void exec(String[] args) {
        if (args == null || args.length != 1) {
            System.out.println("not valid");
            return;
        }

        Path p = Paths.get(this.storeInfo.getName());
        try {
            Map<String, String> now = new Gson().fromJson(Files.readAllLines(p).get(0), HashMap.class);
            System.out.println(now.get(args[0]));
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
}

■全件取得

[ListCommand.java]
import com.google.gson.Gson;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class ListCommand implements Command {
    private StoreInfo storeInfo;

    public ListCommand(StoreInfo storeInfo) {
        this.storeInfo = storeInfo;
    }

    @Override
    public void exec(String[] args) {
        Path p = Paths.get(this.storeInfo.getName());
        try {
            Map<String, String> now = new Gson().fromJson(Files.readAllLines(p).get(0), HashMap.class);
            System.out.println("\"key\",\"value\"");
            now.forEach((k, v) -> System.out.printf("\"%s\",\"%s\"\n", k, v));
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
}

■1件削除

[RemoveCommand.java]
import com.google.gson.Gson;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

public class RemoveCommand implements Command {
    private StoreInfo storeInfo;

    public RemoveCommand(StoreInfo storeInfo) {
        this.storeInfo = storeInfo;
    }

    @Override
    public void exec(String[] args) {
        if (args == null || args.length != 1) {
            System.out.println("not valid");
            return;
        }

        Gson gson = new Gson();
        Path p = Paths.get(this.storeInfo.getName());
        try {
            Map<String, String> now = gson.fromJson(Files.readAllLines(p).get(0), HashMap.class);
            now.remove(args[0]);
            Files.write(p, gson.toJson(now).getBytes());
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
}

■全件削除

[ClearCommand.java]
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class ClearCommand implements Command {
    private StoreInfo storeInfo;

    public ClearCommand(StoreInfo storeInfo) {
        this.storeInfo = storeInfo;
    }

    @Override
    public void exec(String[] args) {
        try {
            Files.write(Paths.get(storeInfo.getName()), "{}".getBytes());
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
}

■ヘルプ

[HelpCommand.java]
public class HelpCommand implements Command {
    @Override
    public void exec(String[] args) {
        String msg = "\n" +
                "[usage]\n" +
                "キーバリュー形式で文字列情報を管理するコマンドです。\n" +
                "以下のサブコマンドが利用可能です。\n" +
                "\n" +
                "save   ... keyとvalueを渡して保存します。\n" +
                "get    ... keyを渡してvalueを表示します。\n" +
                "remove ... keyを渡してvalueを削除します。\n" +
                "list   ... 保存済みの内容を一覧表示します。\n" +
                "clear  ... 保存済みの内容を初期化します。\n" +
                "help   ... ヘルプ情報(当内容と同じ)を表示します。\n" +
                "end    ... アプリを終了します。\n" +
                "\n";
        System.out.println(msg);
    }
}

■アプリ終了

[EndCommand.java]
public class EndCommand implements Command {
    @Override
    public void exec(String[] args) {
        System.out.println("End!");
        System.exit(-1);
    }
}

まとめ

だんだん雑になっていくのを自覚・・・。

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4