お題
前回、Golangで書いた簡易ツール「ローカルJSONファイルに保存するキーバリューストア」のJava版。
シリーズ物としては、もともと複数言語で同じ内容のプログラムを書いて比較した「簡単なツール作成を通して各プログラミング言語を比較しつつ学ぶ」からの改善版。
試行Index
- 第1回:簡単なツール作成を通して各プログラミング言語を比較しつつ学ぶ
- 第2回:【改善編】簡単なツール作成を通してRubyを学ぶ
- 第3回:【改善編】簡単なツール作成を通してPython3を学ぶ
- 第4回:【改善編】簡単なツール作成を通してGolangを学ぶ
- 第5回:【改善編】簡単なツール作成を通してJavaを学ぶ
- 第6回:【改善編】簡単なツール作成を通してScalaを学ぶ
- 第7回:簡単なツール作成を通してRustを学ぶ
- 第8回:【改善編】簡単なツール作成を通してRustを学ぶ
実装・動作確認端末
# 言語バージョン
$ 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);
}
}
まとめ
だんだん雑になっていくのを自覚・・・。