シーケンスステートマシン
コマンドライン引数を解析しようとしました。プログラムには4つの引数が必要です。私は議論を繰り返します。引数がオプションの場合、オプションを処理します。それ以外の場合、引数は必須引数の1つです。必要な引数を読み取るには、ある種のステートマシンが必要です。最初のケースでは、最初の引数を読み取る必要があります。2番目のケースでは、2番目の引数などです。
私が書いたProc
再び返すだけで1つのメソッドを持つクラスをProc
クラスを。
static abstract class Proc {
abstract Proc exec (String arg);
}
これにより、いくつかのアクションを実行し、次に何をすべきかを定義できます。
- データベースホストを保存してから名前を読み取る
- データベース名を保存してからユーザーを読み取る
- dbユーザーを保存してからxmlファイルを読み取ります
- xmlファイルを保存してから何も保存しない
しかし、すべてのクラスオーバーヘッドのため、読みにくいです。
Proc proc = new Proc () {
Proc exec (String arg) {
db_host = arg;
return new Proc () {
Proc exec (String arg) {
db_name = arg;
return new Proc () {
Proc exec (String arg) {
db_user = arg;
return new Proc () {
Proc exec (String arg) {
xml_file = arg;
return null;
}
};
}
};
}
};
}
};
コードを単純化する方法はありますか?Lambdasを試しましたが、Lambdasは最終変数しか使用できないようです。これは、値を格納するときに少し役に立たないものです。
完全な例:
public class Import
{
static String db_host = null;
static String db_port = "5432";
static String db_name = null;
static String db_user = null;
static String xml_file = null;
static void usage ()
{
System.err.println ("Usage: Import [-p PORT] HOST DATABASE USER FILE");
}
static abstract class Proc {
abstract Proc exec (String arg);
}
static void parse_args (String[] args)
{
Proc proc = new Proc () {
Proc exec (String arg) {
db_host = arg;
return new Proc () {
Proc exec (String arg) {
db_name = arg;
return new Proc () {
Proc exec (String arg) {
db_user = arg;
return new Proc () {
Proc exec (String arg) {
xml_file = arg;
return null;
}
};
}
};
}
};
}
};
try {
for (int i = 0; i < args.length; i++)
switch (args[i]) {
case "-p":
db_port = args[++i];
break;
case "-h":
usage ();
break;
default:
proc = proc.exec (args[i]);
}
}
catch (Exception ex) {
throw new Error ("Can not parse args!", ex);
}
}
public static void main (String[] args)
{
parse_args (args);
System.err.println ("db_host: " + db_host);
System.err.println ("db_port: " + db_port);
System.err.println ("db_name: " + db_name);
System.err.println ("db_user: " + db_user);
System.err.println ("xml_file: " + xml_file);
}
}
回答
まず、Javaの命名規則に従う必要があります。
第二に、できるという理由だけで名前を短くしないでください。たとえば、「Proc」は何の略ですか?手順?プロセッサー?名前が長くなっても読みやすさはそれだけの価値があります!
static String db_host = null;
メンバーは、のようなstatic
ものであってはならず、理想的にはpackage-private
、のような、protected
またはprivate
意図を明確に示す以外の資格を持っている必要があります。
必要な引数を読み取るには、ある種のステートマシンが必要です。
それはあなたが与えられた要件ですか、それともあなたが持っていた仮定ですか?それはまったく真実ではないからです。全体として、システムは完全に不必要に複雑であり、明白な理由や利点はありません。ステートマシンのようにも見えません。関数呼び出しの非常に複雑なチェーンにすぎません。
コマンド引数解析用のステートマシンは次のようになります。
for (int index = 0; index < args.length; index++) {
String arg = args[index];
if (arg.equals("-p") && databaseHost == null) {
arg = args[++index];
databasePort = arg;
} else if (databaseHost == null) {
databaseHost = arg;
} else if (databaseUsername == null) {
databaseUsername = arg;
} else if (databasePassword == null) {
databasePassword = arg;
} else if (inputFile == null) {
inputFile = arg;
}
}
そしてそれでさえ奇妙に複雑です。
必要なのは、機能する最も単純なソリューションであり、それは引数のハードコードされた抽出です。
if (args[0].equals("-h")) {
printHelp();
return;
}
int portProvidedOffset = 0;
if (args[0].equals("-p")) {
databasePort = args[1];
portProvidedOffset = 2;
}
databaseHost = args[portProvidedOffset + 0];
databaseUsername = args[portProvidedOffset + 1];
databasePassword = args[portProvidedOffset + 2];
inputFile = args[portProvidedOffset + 3];
もちろん、これはきれいではありませんが、回避できる最も簡単なソリューションです。もちろん、配列の長さが4要素以上であるかどうかを事前に確認する必要があります。その後、すべてのパラメータが設定されているかどうかを確認し、設定されていない場合は、エラーで終了します。
より洗練されたソリューションが必要な場合は、完全な引数パーサーを自分で作成する必要があります。これはそれほど簡単なことではありません。