JavaでConsoleアプリ(その2の2)


アレを挙げたあと、id:AWAWAの人とid:daisuke-mの人から、コメントとSkypeでそれぞれ突っ込みを入れて貰えたので手直ししました。おもに以下の点について。

  1. static使いすぎ。
  2. Java6を使っているなら、System.console()使えば良いジャン。じゃないとパスワード入力のところがダサいよ。
  3. コマンドをエントリ・ポイント内にifで分岐させるってどーいうこと?

1については、「簡単なアプリとは言えども、どこで何をされるか分からない領域の取り扱いは気をつけようね」という事なので、RunnableをImplementsしました。
2については、Eclipseで開発しているためか、System.console()からnullが返されてしまい断念><
3はMainが落ち着いてから別のクラスなりに切り出す予定だったので、予定通りCommandというクラスに切り出しました。(今後、TwitterAPIへのアクセスは、すべてCommandのサブクラスに実装していくことになります)


当初はConsoleMainというクラスだけだったこの子も、twittercui.ConsoleMain、twittercui.commands.Command、twittercui.commands.ShowMeCommandの3つになりました。もう1つくらいクラスが増えたら、もうここに張り付けても見辛いだけなのでどこかのリポジトリに上げないとですね。

package twittercui;
import static java.lang.System.in;
import static java.lang.System.out;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import twittercui.commands.Command;


/**
 * Console main.
 * @author itengineer
 *
 */
public class ConsoleMain implements Runnable {

  private static final String CONSOLE_TITLE = "[ConsoleApp]";
  private static final String WAIT_SUBJECT = CONSOLE_TITLE + "> ";
  private static final int ID_INPUT_REQUIRED_MAX_COUNT = 2;
  private static final int PASSWORD_INPUT_REQUIRED_MAX_COUNT = 2;
  private static BufferedReader input;

  /**
   * Entry point.
   * IDのみ起動パラメータで受け取る。
   * @param args
   */
  public static void main(String[] args) {
    new ConsoleMain(args).run();
  }
  private String[] args;
  private int idUninputCount;
  private int pwUninputCount;
  public ConsoleMain(String[] args) {
    this.args = args;
  }
  /**
   * コンソール
   */
  @Override
  public void run() {
    try {
      input = new BufferedReader(new InputStreamReader(in));

      // ID入力確認
      String id = getId(args);
      if ("".equals(id)) {
        return;
      }
      // パスワード入力確認
      String pw = getPassword();
      // if (pw.equals("")) {
      if ("".equals(pw)) {
        return;
      }
      // ログイン

      // 起動
      out.println("Hi!\nI'm Console application.");
      out.print(WAIT_SUBJECT);
      while (true) {
        String[] inParams = input.readLine().split(" ");
        String commandCode = inParams[0];
        if ("bye".equals(commandCode)
            || "exit".equals(commandCode)
            || "quit".equals(commandCode)) {
          // サヨナラサヨナラ
          break;
        } else {
          // 終了以外の機能
          Command command = Command.getCommand(id, pw, inParams);
          if (command == null) {
            // Commandが得られなかった場合
            out.println("Command["+ commandCode + "] is not found! ...");
          } else {
            String returnValue = command.execute();
            out.println(CONSOLE_TITLE + returnValue);
          }
        }
        // 入力値に対する出力
        out.print(WAIT_SUBJECT);
      }
      out.println("Bye.");
      input.close();
    } catch (IOException e) {
      e.printStackTrace();
      return;
    }
  }
  /**
   * IDの入力値を得る。
   * @param args
   * @return
   * @throws IOException
   */
  private String getId(String[] args) throws IOException {
    String id = "";
    if (args.length == 0 || "".equals(args[0])) {
      out.println(CONSOLE_TITLE + "Please input your id.");
      out.print(CONSOLE_TITLE + "Twitter ID:");
      id = input.readLine();
      if ("".equals(id)) {
        // ID未入力をカウント。
        this.idUninputCount++;
        if (ID_INPUT_REQUIRED_MAX_COUNT <= this.idUninputCount) {
          // disる。
          out.println("ゴルア!");
        } else {
          id = getId(args);
        }
      }
    } else {
      id = args[0];
    }
    return id;
  }
  /**
   * Passwordの入力値を得る。
   * @return
   * @throws IOException
   */
  private String getPassword() throws IOException {
    out.println(CONSOLE_TITLE + "Please input your password.");
    out.print(CONSOLE_TITLE + "PASSWORD: ");
    String pw = input.readLine();
    if ("".equals(pw)) {
      // Password未入力をカウント
      this.pwUninputCount++;
      if (PASSWORD_INPUT_REQUIRED_MAX_COUNT <= this.pwUninputCount) {
        // disる。
        out.println("ゴルア!!");
      } else {
        pw = getPassword();
      }
    }
    return pw;
  }
}
package twittercui.commands;


/**
 * AbstractFactoryで各命令に対応するヨテイ。
 * @author itengineer
 *
 */
public abstract class Command {

  protected String id;
  protected String pw;
  /**
   * コンストラクタ
   * @param id
   * @param pw
   */
  public Command(String id, String pw) {
    this.id = id;
    this.pw = pw;
  }
  /**
   * AbstractFactoryなアレ。
   * @param args
   * @return
   */
  public static Command getCommand(String id, String pw, String[] args) {
    Command command = null;
    if (args == null || args.length == 0) {
      return null;
    }
    String commandCode = args[0];
    if ("showme".equals(commandCode)) {
      command = new ShowMeCommand(id, pw);
    }
    return command;
  }
  /**
   * 実際のアレ。
   * @return
   */
  abstract public String execute();
}
package twittercui.commands;


/**
 * ユーザ認証で使ったIDとパスワードを惜しげもなく披露する。
 * @author itengineer
 *
 */
public class ShowMeCommand extends Command {

  /**
   * コンストラクタ
   * @param id
   * @param pw
   */
  public ShowMeCommand(String id, String pw) {
    super(id, pw);
  }

  /**
   * ご開陳する。
   */
  @Override
  public String execute() {
    return "Your information is ...\nID:[" + this.id + "]\nPW:[" + this.pw + "]";
  }
}


個人的に、Command#getCommandのパラメータにIDとパスワードがあるのが美しくないような気がしてなりません><
でも、Context的なDTOを使ってあれこれするのもまた変な気がするサイズのものなので、「優先度低めで思いついたら」レベルでいつか改善できたらな良いです。


(追記)

  • staticな変数inputを最後にCloseする。
  • ifの条件表記を統一。

の2点を直しました。