UMLの書き方読み方~シーケンス図~

シーケンス図の書き方

IT専科というサイトを参考にするとシーケンス図とは。。。

シーケンス図とは、クラスやオブジェクト間のやりとりを時間軸に沿って表現する図です。

アジャイルモデリング(AM)というサイトを参考にして学習します。そして、下のような図があります。

シーケンス図1

これは、手書きなのでちょっとわかりずらいですが、ポイントとしては、次のような部分です。

  1. 登場人物が3人(アプリケーションを含む)

  2. それぞれの時間軸が縦に伸びている

  3. 縦軸に対して、横軸は「何かしらの動きを示す」

大まかに、このようなところがポイントになります。
アジャイルモデリング(AM)というサイトには、下のような説明がありました。

これがシーケンス図と呼ばれている理由は明らかでしょう。ロジックの実行順序(シーケンス)がメッセージ(横向きの矢印)の順序で示されています。最初のメッセージは左上から始まり、次のメッセージはそのすぐ下に書く、というように表していきます。

シーケンス図の内訳

サービスレベルのシーケンス図

参考サイトには、下のような説明があります。

図の上部に横に並んでいる箱は、分類子またはそのインスタンスを表します。この分類子は通常、ユースケース、オブジェクト、クラス、またはアクターです。オブジェクトとクラスにはメッセージを送ることができるため(オブジェクトは操作の呼び出しを通じて、クラスは静的操作の呼び出しを通じてメッセージに応答します)、これらをシーケンス図に含めるのは筋が通っています。アクターも、利用シナリオを開始したり、利用シナリオで能動的な役割を果たすので、シーケンス図に含めることができます。オブジェクトにはUML標準の「名前: クラス名」という書式でラベルをつけます。この名前は必須ではありません(図で名前の付いていないオブジェクトのことを無名オブジェクトと呼びます)。クラスには「クラス名」という書式でラベルを付け、アクターには「アクター名」の書式で名前を付けます。オブジェクトのラベルには下線が引かれていますが、クラスとアクターには引かれていないことに注意してください。たとえば、図3の学生オブジェクトにはある学生という名前が付けられています。これが名前付きオブジェクトです。それに対してゼミのインスタンスは無名オブジェクトです。Studentのインスタンスに名前が付けられているのは、複数の場所でメッセージのパラメータとして使われているためです。ゼミのインスタンスの方は、図の他の場所で参照する必要がないので、無名にしておくことができます。図2では、学生クラスが永続性フレームワーククラスにメッセージを送っています(永続性フレームワーククラスには\<\>というステレオタイプを付けてもよかったのですが、図を簡潔にしておくために付けませんでした)。クラスに対して送られたメッセージは、すべて静的メソッドとして実装します。これについては後で説明します。

まとめると次のようになります。
上の図で示した「登場人物」は、「ユースケース、オブジェクト、クラス、またはアクター」

  • ユースケース:「申込用紙を書く」とか、「送信ボタンを押下」などのように人の動き、作業を示す。
  • オブジェクト:クラスとかインスタンスのこと(厳密にはインスタンスの事)
  • アクター  ;人のモデル(人を示す絵)

そして、オブジェクトはメッセージを送信することができるので、ほかのオブジェクトを呼び出し、何かしらの処理を行わせることができます。その処理が終わったら、また元のオブジェクトの線に戻ってきます。下のような矢印のことです。

※引用した文言の中に「すべて静的メソッドとして実装します」とありますが、これはこちらのサイトでそのように実装しているということです。別に静的メソッドである必要はありません

そして、上記の手書きの画像をきれいに書くと下のようになるようです。

ここで、シーケンス図に使用される図をまとめると、IT専科というサイトの表を借りると下のようになります。

構成要素一覧
要素 表示形式 意味
ライフライン(Lifeline) ライフライン 記号 使用するオブジェクトやクラスを表現します。どちらか一方なら省略可能です。
実行仕様(ExecutionSpecification) 実行仕様 記号 生成されているライフラインが実行状態であることを意味します。
停止(Stop) 停止 記号 生成されたライフライン自体の消滅を意味します。
メッセージ(Message) 同期(Synchronous)メッセージ 同期メッセージ 記号 送り先のライフラインの実行に同期されるメッセージを意味します。メッセージ名には具体的な関数やINCLUDEディレクティブ等を記入します。
非同期(Asynchronous)メッセージ 非同期メッセージ 記号 送り先のライフラインの実行に同期されないメッセージを意味します。メッセージ名には具体的な関数やINCLUDEディレクティブ等を記入します。
応答(Reply)メッセージ 応答メッセージ 記号 送り先のライフラインから送り手への戻り値を意味します。メッセージ名には戻り値を格納する具体的な変数名等を記入します。
ファウンド(Found)メッセージ ファウンドメッセージ 記号 図解上にない送り手から送られた、もしくは送り手がダイアグラム上にないことを意味します。
ロスト(Lost)メッセージ ロストメッセージ 記号 意図された受け手に送られていない、もしくは受け手がダイアグラム上にないことを意味します。

▲PageTop

制御構造の記述

シーケンス図では、制御構造を表現するために「複合フラグメント」を使用します。種類および、記述例は次の通りです。

複合フラグメントの種類

複合フラグメントには、次の種類があります。

複合フラグメント一覧
InteractionOperator 読み 意味
ref 相互作用使用(InteractionUse) 別のシーケンス図を参照することを表します。
alt オルタナティブ(Alternative) 分岐処理を表します。
opt オプション(Option)

条件を満たした場合のみ実行される処理を表します。

par パラレル(Parallel) 並列処理を表します。
loop ループ(Loop) ループ(繰り返し)処理を表します。
break ブレイク(Break) 処理の中断を表します。
critical クリティカル(Critical) マルチスレッド環境での同期処理など、排他制御を表します。
assert アサーション(Assert) 処理が妥当であるための定義を表します。
neg 否定(negation)

本来、実行されるはずがない処理(メッセージ)であることを表します。

ignore 無効(ignore) あまり重要な処理(メッセージ)ではないことを表します。
consider 有効(Consider) 重要な処理(メッセージ)であることを表します。

基本的な処理を表現する

ここでいう「基本的な処理」とは、次のものを指します。

  1. 参照(REF)
  2. 条件分岐(ALT)
  3. 条件判断(OPT)
  4. 並列処理(PAR)
  5. 反復処理(LOOP
  6. 中断(BREAK
  7. クリティカルセッション(CRITICAL
  8. アサート(ASSERT
  9. 不正なシーケンス(NEG
  10. 無効(IGNORE
  11. 有効(CONSIDER

このような形で記述します。あとは、どのような動きを表現したいのか?を考えるだけです。
しかし、これらの「動き」を考えるためには、プログラミングの基礎を理解する必要があります。
※よかったら参考にどうぞ、Java Basic学習フロー

具体的には、「じゃんけんゲーム」を作成しようと考えたときには、どのような画面で、ユーザーの入力はどのように行うのか?などの「人間レベル」の動きから、「入力値からどのような処理をして勝敗の判定を行うか?」という「プログラムレベル」の動きを考える必要があるためです。

こんなところで失礼します。

でわでわ。。。

FreeTts エラー ~mbrola.base

FreeTtsエラー

下のような警告があり、修正してみました。

System property "mbrola.base" is undefined. Will not use MBROLA voices.

インストールしたFreeTtsのディレクトリに「mbrola」というフォルダがあったのでシステムプロパティにそのパスを渡して実行すると下のようにエラーが出ました。

System.setProperty("mbrola.base", "D:\\Apps\\freetts-1.2\\mbrola");

Make sure you FULLY specify the path to
the MBROLA directory using the mbrola.base
system property.

調べてみると、JDKのあるフォルダlibの下にmbrola.jarをコピーしてやればOKということでした。

しかし、MBROLAの音声ファイル(Voice)がダウンロードできなかったので、(502 Bad Gateway)打つ手なしと判断しました。。。

IntelliJ IDEA Maven リポジトリからロードできない

Mavenでソースをロードできない

IntelliJ IDEAを使用してpom.xmlにMavenリポジトリからソースをロードしようとすると下のような文言が出てロードできない事象にあいました。

依存関係 'com.ibm.icu:icu4j:2.9.1' が見つかりません

これは、間違っているので、エラーになっているのですが。正しくは、下のような形でpom.xmlを書きます。

<dependencies>
    <dependency>
        <groupId>com.ibm.icu</groupId>
        <artifactId>icu4j</artifactId>
        <version>2.6.1</version>
    </dependency>
</dependencies>

これでロードできるはずなのですが、出来ない。。。
こちらのページを参考にすると、「Mavenの更新ができていないから」ということでした。
下のような操作を行います。

  1. プロジェクトを右クリック
  2. Mavenを選択
  3. プロジェクトの再ロード

これで、ソース(JARなど)をロードすることができます。

でわでわ。。。

Java Speach APIを学ぶ(遊ぶ)

Java Speach API(JSAPI)

セットアップ

こちらのサイトを参考にインストールしました。
結局はライブラリをインストールする形になりました。

  1. ライブラリをSourceForgeからダウンロードします。

  2. これを展開して中にあるjsapi.exeを実行する

    この例では、D:\ apps \フォルダーが使用されます
    D:\apps\freetts-1.2.1\lib jsapi.exeに移動し て実行します。

  3. これによりjsapi.exeが作成されるようですが、初めからありました。

  4. jsapi.exeのあるディレクトリ(フォルダ)をライブラリとして指定します。

    1. 上部メニューのファイルを選択
    2. プロジェクトの構造を選択
    3. ライブラリの作成
    4. プロジェクトに追加されていることを確認
    5. プロパティファイルをJDK/jre/libに配置します。※ D:\Apps\jdk1.8.0_265\jre\lib

      D:\apps\freetts-1.2.1\speech.properties ファイルを %user.home% または %java.home%/lib フォルダにコピーし ます。このファイルは、JSAPIが使用する音声エンジンを決定するために使用されます。
      具体的には、

実行

参考サイトに載っているコード(java)を三つコピーして作成しました。

  • BriefVoiceDemo.java
  • BriefSpeakable.java
  • BriefListener.java

日本語をしゃべらせる

調べてみると「mbrola」が日本語に対応する声を持っているようです。具体的にはfreettsのフォルダ内にある「mdrola」のことです。
とりあえずは、実行するためのコードを見てみるとMBROLAを使用しているようなので、ロケールをJAPANESEに変更し、動かしてみると...

「Locale.US」を「Locale.JAPANESE」に変更してあります。

//default synthesizer values
SynthesizerModeDesc modeDesc = new SynthesizerModeDesc(
        null,       // engine name
        "general",  // mode name use 'general' or 'time'
        Locale.JAPANESE,  // locale, see MBROLA Project for i18n examples
        null,       // prefer a running synthesizer (Boolean)
        null);      // preload these voices (Voice[])

下のようなエラーが出ました。

System property "mbrola.base" is undefined. Will not use MBROLA voices.
Unable to create synthesizer with the required properties

Be sure to check that the "speech.properties" file is in one of these locations:

mbrolaの設定が良くないようです。なのでMBROLAを調べることにします。

調べていくと次のページを見つけました。ここに細かいところの記載があるので、これを参考にしてみます。

しかし、必要なファイルなどがダウンロードできません。。。。
結局はGithubにありました。

これをダウンロード(ZIP)して見ましたが、これもリンク切れが多く、調査が進みませんでした。。。。

しかし、英語を日本語調で発音させることはできるようです。

やはり、人工知能処理を入れないとできないようです。。。

ここであきらめない!

しかし、既製品のものがあります。FreeTtsも日本語での発音は実現しています。
詳細に関しては、商品化しているであろうため公開されていないと思われます。

やはり、下のような手順で行うのが無難なのかもしれません。

  1. 入力した文字をすべてひらがなに変換
  2. ひらがなをそれっぽい発音をする単語に関連図ける(Mapを使用する)
  3. 各単語をすべてアルファベットに変換
  4. 再生する

こんな方法しか見つかりませんでした。もちろん、TTSサービスを使用できるサイトなどたくさんあります。
しかし、Javaで自力で音声を再生したかったのです。。。

粘ってみた

とりあえずのところ、FreeTtsでは日本語をスピーチさせることが実現できない状態ですが、他にMaryTtsというライブラリ?がありました。
ソースをコンパイルして、起動すればサーバーとして起動できるようです。ここら辺に解決の糸口を見つけたいと思います。

MaryTts

MaryTtsをpom.xmlに追加して、MavenでJARを追加しました。
そして、ここにMaryTtsの新言語の追加方法が書いてありました。
GithubのWikiページですね。
ここを読み進めてみます。

しかし、色々と躓き断念することにしました。。。

FreeTtsで頑張る

結局のところ上に記載した方法で実装することにしました。

  1. 入力した文字をすべてひらがなに変換
  2. ひらがなをそれっぽい発音をする単語に関連図ける(Mapを使用する)
  3. 各単語をすべてアルファベットに変換
  4. 再生する

そして、必要になる(あったら無難な)ライブラリを使用することにします。

  • ICU4J:漢字ひらがな変換ライブラリ:

漢字ひらがな変換ライブラリ

pom.xmlの設定では、リポジトリの指定と、依存関係の指定で追加できました。

<repositories>
    <repository>
        <id>icu4j</id>
        <url>https://repo1.maven.org/maven2/</url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>com.ibm.icu</groupId>
        <artifactId>icu4j</artifactId>
        <version>2.6.1</version>
    </dependency>
</dependencies>

ICU4Jに関しては、こちらの記事を参照ください。

なんだかんだと実装した結果「本日は晴天なり」としゃべることに成功しました。

実装する(テストクラス)

まずは、音声に変換するための文字列をマッピングします。なので、初めのBriefVoiceDemoクラスで書く発音の文字列を決めます。
次のような感じで発音できました。

これらの文字列と各カタカナを関連付けていきます。要領としては以下の通りです。

private static final String[] KANA_LIST = {"ア", "イ", "ウ", "エ", "オ", // 1
                                            "カ", "キ", "ク", "ケ", "コ", // 2
                                            "ガ", "ギ", "グ", "ゲ", "ゴ", // 3
                                            "サ", "シ", "ス", "セ", "ソ", // 4
                                            "ザ", "ジ", "ズ", "ゼ", "ゾ", // 5
                                            "タ", "チ", "ツ", "テ", "ト", // 6
                                            "ダ", "ヂ", "ヅ", "デ", "ド", // 7
                                            "ナ", "ニ", "ヌ", "ネ", "ノ", // 8
                                            "ハ", "ヒ", "フ", "ヘ", "ホ", // 9
                                            "バ", "ビ", "ブ", "ベ", "ボ", // 10
                                            "マ", "ミ", "ム", "メ", "モ", // 11
                                            "ヤ", "ユ", "ヨ", // 12
                                            "ラ", "リ", "ル", "レ", "ロ", // 13
                                            "ワ", "ヲ", "ン", // 14
                                        };

String[] moji = {"ah", "yee" , "hu", "a", "oh" // 1
    , "kah", "kee", "ku", "ckea", "koh" // 2
    , "gaah", "gy", "goo", "gue", "goh" // 3
    , "saeh", "see", "su", "thea", "soh" // 4
    , "zaeh", "zee", "zoo", "zea", "zoh" // 5
    , "taeh", "tiee", "tsu", "te", "toh" // 6
    , "daeh", "dgee", "do", "de", "doh" // 7
    , "naeh", "niee", "nuh", "nea", "noh" // 8
    , "haeh", "hiee", "hu", "hea", "hoh" // 9
    , "baeh", "bee", "boo", "be", "boh" // 10
    , "maeh", "miee", "muh", "me", "moh" // 11
    , "yaeh", "yu", "yoh" // 12
    , "ra", "ri", "ru", "re", "roh" // 13
    , "wa", "oh", "um"}; // 14

// サイズ(長さ)はおなじなので
for (int i = 0; i < KANA_LIST.length; i++) {
    String key = KANA_LIST[i];
    String value = moji[i];
    talkMap.put(key, value);
}

これで入力文字を発音用の文字列に変換し再生します。作成したクラスは次の通りです。

public class BriefVoiceClsTest {
    private static BriefVoiceCls target;
    @BeforeClass
    public static void init() {
        target = new BriefVoiceCls();
    }

    @Test
    public void testTalkVoice() {
        target.execute("本日は晴天なり");
    }
}

<実行結果>

でわでわ。。。

でわでわ。。。

<IntelliJ IDEAを操作して時の動画リスト>

JavaCV エラー (-215:Assertion failed) !image.empty() in function ‘cv::imencode’ ]

OpenCVでエラー発生

下のようなエラーメッセージです。

Caused by: CvException [org.opencv.core.CvException: cv::Exception: OpenCV(4.4.0) C:\build\master_winpack-bindings-win64-vc14-static\opencv\modules\imgcodecs\src\loadsave.cpp:919: error: (-215:Assertion failed) !image.empty() in function 'cv::imencode'
]
at org.opencv.imgcodecs.Imgcodecs.imencode_1(Native Method)
at org.opencv.imgcodecs.Imgcodecs.imencode(Imgcodecs.java:378)
at zenryokuservice.opencv.fx.learn.LearnOpenCv.createBufferedImage(LearnOpenCv.java:137)
at zenryokuservice.opencv.fx.learn.LearnOpenCv.execute(LearnOpenCv.java:72)
at zenryokuservice.opencv.fx.controller.TestingCvController.clickExecute(TestingCvController.java:89)
... 59 more

実行したときのコード

        // 表示するイメージを取得
        URL url = getClass().getResource("/charactors/myFace.png");
        URL url_kanaB = getClass().getResource("/charactors/kanabo.png");
System.out.println(url.getPath());
System.out.println(url_kanaB.getPath());
        // 表示イメージを読み取る
        Mat charactor = Imgcodecs.imread(url.getPath());
        Mat gray = Imgcodecs.imread(url.getPath(), Imgcodecs.IMREAD_GRAYSCALE);
        Mat kanaImg = Imgcodecs.imread(url_kanaB.getPath());
        System.out.println(kanaImg);
        optImg = createBufferedImage(kanaImg, ".png");

<createBufferedImage()>

    private BufferedImage createBufferedImage(Mat img, String ext) throws IOException {
        MatOfByte matOfByte = new MatOfByte();
        Imgcodecs.imencode(ext, img, matOfByte); -> ここでエラー
         return ImageIO.read(new ByteArrayInputStream(matOfByte.toArray()));
    }

エラーになるのは「Imgcodecs.imencode(ext, img, matOfByte);」の部分です。

原因として考えられること

  1. imgを渡すのに、データが不正(画像が読み込めていないなど)
  2. ファイル拡張子が違う
  3. Matの出力結果から怪しいところをみる

    Mat [ -1-1CV_8UC1, isCont=false, isSubmat=false, nativeObj=0x1c407d00, dataAddr=0x0 ]

このうちの「dataAddr=0x0」というのがnull参照になっている?と疑問に思いました。

解決

結局のところは、BufferedImageを作るのに、Matクラスを使う必要がないので、したのようにImageIOを使用することにしました。

BufferedImage buf = ImageIO.read(url);

これで一応の解決をしました。

jdk.nashorn.internal.runtime.ParserException: :5:4 Expected ; but found isStart

ScriptEngineを使ったら

JavaからJavaScriptを呼ぼうとして、エラーが出ました。
下のようなエラーです。

javax.script.ScriptException: :5:4 Expected ; but found isStart
let isStart = false;
^ in at line number 5 at column number 4
at jdk.nashorn.api.scripting.NashornScriptEngine.throwAsScriptException(NashornScriptEngine.java:470)



Caused by: jdk.nashorn.internal.runtime.ParserException: :5:4 Expected ; but found isStart
let isStart = false;

実装したJSは「じゃんけんゲーム」です。調べてみるとglobal変数を使うなら次のように、eval()を使用して定義するとよいみたいだ。参考サイト
具体的に「engine.eval("var value='Hello '+name+'!';");」の部分です。

// Obtain an instance of JavaScript engine
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");

try {
    // Set value in the global name space of the engine
    engine.put("name","Nashorn");
    // Execute an hardcoded script
    engine.eval("var value='Hello '+name+'!';");
    // Get value
    String value=(String)engine.get("value");
    System.out.println(value);
} catch (ScriptException ex) {
    // This is the generic Exception subclass for the Scripting API
    ex.printStackTrace();
}
/********************
 * じゃんけんゲーム    *
 ********************/
 /** グローバル変数 */
let isStart = false;
let cpuTe = 0;
let  janAudi = new Audio("/audio/sample/Jan.m4a");
let  kenAudi = new Audio("/audio/sample/Ken.m4a");
let  ponAudi = new Audio("/audio/sample/Pon.m4a");
let  aikoAudi = new Audio("/audio/sample/Aiko.m4a");
let  showAudi = new Audio("/audio/sample/Show.m4a");
let isAiko = false;
let isPlay = false;

/** オーディオファイル選択 */
function selectAudi() {
    // 音の再生中、じゃんけんルーレットが回っている最中は何もしない
    if (isPlay || isStart) {
        return;
    }
    // 再生ファイル(オブジェクト)を開放する
    janAudi = null;
    kenAudi = null;
    ponAudi = null;
    aikoAudi = null;
    showAudi = null;

    // 選択している先生の名前を取得する
    var selectBox = document.getElementById("select");
    var teacherName = selectBox.value;
    // 再生ファイルを再度セットする
    janAudi = new Audio("/audio/" + teacherName + "/Jan.m4a");
    kenAudi = new Audio("/audio/" + teacherName + "/Ken.m4a");
    ponAudi = new Audio("/audio/" + teacherName + "/Pon.m4a");
    aikoAudi = new Audio("/audio/" + teacherName + "/Aiko.m4a");
    showAudi = new Audio("/audio/" + teacherName + "/Show.m4a");
}

 /** スタートボタン押下時の処理 */
async  function start() {
    if (isStart) {
        return;
    }
    let resImg = document.getElementById("resultImage");
    resImg.style.display = "none";

     if (isAiko == false) {
         janken();
     }
     isStart = true;
     loopCpuTe();
 }

/** CPUの手をルーレットのように回す */
async function loopCpuTe() {
     let pathArray = ["/img/Goo.png", "/img/Choki.png", "/img/Pa.png"];
     let img = document.getElementById("targetImage");
     img.src = pathArray[0];

     var count = 0;
     while (isStart) {
         cpuTe = count;
         img.src = pathArray[cpuTe];
         await sleep(150);
         count++
         if (count > 2) {
            count = 0;
         }
     }
}

/** 各手を押下したときの処理 */
async function stop(te) {
    if (isPlay) {
        return;
    }
    janAudi.paused = true;
    kenAudi.paused = true;

    let result = 0;

    setCpuTe();
    if (te == cpuTe) {
        isPlay = true;
         setWords("あいこで!");
        aikoAudi.play();
        await sleep(1500);
        isPlay = false;
        isAiko = true;
        start();
        return;
    }

    pon();
    isStart = false;
    isAiko = false;
    if ((te + 1) % 3 == cpuTe) {
        result = 1; // YOU_WIN
    }
    if ((te + 2) % 3 == cpuTe) {
        result = 2; // YOU_LOOSE
    }
    await sleep(500);

     let img = document.getElementById("resultImage");
     img.style.disoplay = "block";

    var message = document.getElementById("words");
    if (result == 1) {
        message.innerText = "YOU WIN";
        img.src = "/img/YouWin.png";
    } else if (result == 2) {
        message.innerText = "YOU LOOSE";
        img.src = "/img/YouLoose.png";
    }
    img.style.display = "block";
}

/** 処理を一時停止する */
function sleep(mSec) {
    return new Promise(resolve => setTimeout(resolve, mSec));
}

 /** じゃんけんの再生 */
 async function janken() {
     isPlay = true;
     setWords("じゃん!");
     janAudi.play();
     await sleep(800);
     setWords("けん!");
     kenAudi.play();
     await sleep(300);
     isPlay = false;
 }

/** ポン!(ショ!)の再生 */
 async function pon() {
     isPlay = true;
     if (isAiko) {
         showAudi.play();
         setWords("SHOW!");
     } else {
       ponAudi.play();
       setWords("ポン!");
     }
     isPlay = false;

 }

/** CPUの手を表示 */
function setCpuTe() {
     let pathArray = ["/img/Goo.png", "/img/Choki.png", "/img/Pa.png"];
     let img = document.getElementById("targetImage");
     img.src = pathArray[cpuTe];
}

/** 文言の表示切り替え */
function setWords(moji) {
    let tag = document.getElementById("words").innerText = moji;
}

補足

こちらからJavaで扱うJavaScritptのチュートリアルが見れました。
でわでわ。。。

Java Servlet web.xml ~デプロイメント記述子の書き方~

イントロダクション

Java Servletでの実装には、web.xmlが欠かせません。

しかし、あまりいじらないのでメモがてらに内容をまとめておきます。

デプロイメント記述子の書き方

<?xml version="1.0" encoding="ISO-8859-1"?> 
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
    <!-- 初めに表示するファイルのリスト -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>

   <!-- エラーページ -->
    <error-page>
        <error-code>500</error-code>
        <location>エラーページのファイル</location>
    </error-page>

    <!-- フィルターの設定 -->
    <filter>
        <filter-name>フィルター名  </filter-name>
        <filter-class>フィルターの完全クラス名  </filter-class>
        <init-param>
            <param-name></param-name>
            <param-value></param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>フィルター名</filter-name>
        <url-pattern>対応するURLパターン</url-pattern>
    </filter-mapping>

    <!-- サーブレットの設定 -->
    <servlet> 
        <servlet-name>サーブレットの名前</servlet-name>
        <servlet-class>サーブレットクラスの完全クラス名</servlet-class>
   </servlet> 
   <!-- サーブレットの名前でservletタグのクラスと、URLを関連付ける -->
   <servlet-mapping>
        <servlet-name>サーブレットの名前</servlet-name>
        <url-pattern>表示するURL(/helloなど)</url-pattern>
   </servlet-mapping>

    <!-- 初期処理 -->
    <listener>
       <listener-class>servlet.CounterListener</listener-class>
    </listener>
</web-app>

リスナーに関して

リスナーは以下のようなインターフェースを実装することでそれぞれの処理を行うようだ。

  1. 初回起動時: javax.servlet.ServletContextListener
    2.セッションの操作時:javax.servlet.http.HttpSessionListener
  2. リクエストの操作時: javax.servlet.ServletRequestListener

でわでわ。。。

Java H2DB 接続が壊れています

H2DBエラー

H2DBをTomcatで使用し始めて、DBコネクションを取得しようとしたときに下のようなエラーが出ました。

org.h2.jdbc.JdbcSQLNonTransientConnectionException: 接続が壊れています: "java.net.SocketTimeoutException: > connect timed out: localhost"
Connection is broken: "java.net.SocketTimeoutException: connect timed out: localhost" [90067-200]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:622)
at org.h2.message.DbException.getJdbcSQLException(DbException.java:429)
at org.h2.message.DbException.get(DbException.java:194)
at org.h2.engine.SessionRemote.connectServer(SessionRemote.java:439)
at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:321)
at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:173)
at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:152)
at org.h2.Driver.connect(Driver.java:69)

考えられる原因は、以下の通りです。

  1. H2DBをサーバーモードで使用していない
  2. URLの指定方法がおかしい

実際のコードは下のようなものです。

try {
    Class.forName("org.h2.Driver");
    con = DriverManager.getConnection("jdbc:h2:tcp://localhost/~/database");
    Statement stmt = con.createStatement();
    stmt.execute(SQLConst.CREATE_USER_TBL);
    stmt.execute(SQLConst.CREATE_ITEM_TBL);
    stmt.execute(SQLConst.CREATE_ITEM_TYPE_TBL);
} catch (SQLException e) {
    e.printStackTrace();
    System.exit(-1);
} catch (Exception e) {
    e.printStackTrace();
    System.exit(-1);
}

解決

ちょっと特殊な解決方法でしたが、シンプルにH2DBをサーバーで起動してやれば大丈夫でした。
具体的な手順は以下の通りです。

H2DBのサーバーを起動する

H2をインストールしたディレクトリにある、「h2.bat」もしくは「h2.sh」を起動すればOK

もしくは、Windowsの場合にウィンドウズボタンからのアプリケーション「H2Console」を起動して対象のDBファイルにアクセスすればOK

Java ポリモーフィズム(多様性) 〜実装をしてみる〜

イントロダクション

前回の学習を踏まえて、抽象クラス、インターフェースを使用して簡単なプログラムを作成してみようと考えています。

頭で理解しても、実践が伴わなければものになりません

前回の学習ポイント

継承関係を作り、下の図のようなクラス関係を作成しました。

そして、RpgCharacterクラスは、抽象クラスです。

以下のような手順で作成しました。

!. 通常のlクラスを作成する(RpgCharacter)

  1. 必要なプロパティ(属性を追加する)
  2. それぞれのGeter&Setterを作成
  3. このクラスを継承するすべてのクラスに共通するであろうメソッドを作成
    /** にげる */
    public void escape() {
    System.out.println(this.name + "は逃げ出した");
    }
  4. 実装する各クラスによって別の処理をさせたいので、抽象メソッドを作成
    /** 抽象メソッド「たたかう」 */
    public void attack(Monster monst);
  5. RpgCharacterクラスを継承して、Wizardクラスなどのクラスを作成する

このように、クラス関係を作ると、RpgCharacterクラスを継承したクラスを全てRpgCharacterクラス型の変数で各クラスオブジェクトを使用することができる。
<RpgCharacterクラス型の変数でいろんなクラスを使う>

public static void main(String[] args) {
        RpgCharacter[] party = new RpgCharacter[2];
        party[0] = new RpgHero("太郎");
        party[1] = new RpgWizard("二郎");

        System.out.println("こんにちは、良いパーティですね。");
        for (int i = 0; i < party.length; i++) {
            System.out.println(party[i].getName() + "さん");
        }
    }

ポリモーフィズムの実装

前回学習したものを実装して、「動くもの」を作成しようというところです。

実装する手順を考える

実装するのには、それなりの時間がかかりますが、やってみることの意義はすごく大きいです。

今回は、キャラクターを作成する処理から実装していこうと考えています。

<作成するものVersion1>

  1. 勇者とモンスターが1回ずつ攻撃する
  2. それぞれの攻撃に対して、各キャラクターのHPを減算する

メインメソッドの実装

ここでは、プログラムをイチから作成する方向で、話を進めていきます。

作成したプログラムは下のような実行結果が得られるように、実装します。

まずは、プログラムを実行するためのクラス(BattleSample.java)を作成します。エラーが出る部分はコメントアウトします。

つまるところは、どのような処理を呼び出したいかを実装します。ちなみにコメントのみでも良いです。

バージョン1の実装

<BattleSample.java-バージョン1

public static void main(String[] args) {
    //勇者クラスの生成 
    RpgHero hero = new RpgHero("勇者");
    // モンスタークラスの生成
    Slime slime = new Slime("スライムA");

    // 勇者の攻撃
    hero.attack(slime);
    // モンスターの攻撃
    slime.attack(hero);
}

このように、勇者クラスと、スライムクラスをインスタンス化してそれぞれのメソッド(コマンド)を呼び出してやるだけのシンプルな実装です。

これで、勇者とスライムの攻防がコンソール(標準出力)へ出力されます。

問題は、RpgHeroクラスとSlimeクラスができていないという部分です(前回の実装を行った人は作成済みです)

RpgHeroクラスを作る

このクラスは、下の図にあるように、RpgCharacterクラスを継承した形で、実装します。

なので、先にRpgCharacterクラスを作成する必要がありますので、こちらのクラスの製造に着手します。

RpgCharacterクラスの製造

このクラスは、湯者だけではなく魔法使いや、盗賊、戦士などのプレーヤー側のキャラクターに共通する部分を実装する
想定で、設計しました。

なので、下のように共通するもの(属性・振る舞い)を実装します。

つまり、現状では、通常のクラスになります。

public class RpgCharacter {
    /** 名前 */
    protected String name;
    /** HP */
    protected int hp;
    /** MP */
    protected int mp;
    /** 性別 */
    protected int sex;
    /** 年齢 */
    protected int age;
    /** 誕生日 */
    protected String birthDay;
   ・
   ・
   ・
}

上の状態では、属性(フィールド変数)を定義した状態です。

このほかに追加したいものもあるので、一度内容を確認します。

<RpgCharacterクラスの設計1>

  1. 属性、名前を持っている
  2. 属性、MPを持っている
  3. 属性、年齢を持っている
  4. 属性、性別を持っている
  5. 属性、誕生日を持っている

個の状態では、共通するプロパティ(属性)のみが定義されています。ほかにも考えれば出てきそうですが。。。

そして、共通するコマンド(メソッド)を定義します。

<RpgCharacterクラスの設計2>

  1. 属性、名前を持っている
  2. 属性、MPを持っている
  3. 属性、年齢を持っている
  4. 属性、性別を持っている
  5. 属性、誕生日を持っている
  6. 振る舞い、にげるを持っている

作成した設計を元に、そのクラスを実装します。

<RpgCharacter>バージョン1

public class RpgCharacter {
    /** 名前 */
    protected String name;
    /** HP */
    protected int hp;
    /** MP */
    protected int mp;
    /** 性別 */
    protected int sex;
    /** 年齢 */
    protected int age;
    /** 誕生日 */
    protected String birthDay;

    /** にげる */
    public void escape() {
        System.out.println(this.name + "は逃げ出した");
    }

フィールド変数には、本来「private」修飾氏をつけるのですが、今回は、子クラスを作成するための親クラスとして作成しているので、「protected」をつけて、継承関係のあるクラスであれば参照可能な形にしています。

そして、Getter, Setterは省略していますので、それぞれのメソッドが実装されているていで話を進めます。

この状態では、「たたかう」メソッドを強制的に継承する子クラスに実装させることができないので、下のような、抽象メソッドを追加します。

    /** たたかう */
    public abstract void attack(RpgCharacter character);

このようにすると、「抽象メソッドは抽象クラス、インターフェースでないともていない」ので、RpgCharacterクラスを通常のクラスから「抽象クラス」に変更します。

public abstract class RpgCharacter {
    /** 名前 */
    protected String name;
    /** HP */
    protected int hp;
    /** MP */
    protected int mp;
    /** 攻撃力 */
    protected int attackPower;
    /** 防御力 */
    protected int diffencePower;
    /** 性別 */
    protected int sex;
    /** 年齢 */
    protected int age;
    /** 誕生日 */
    protected String birthDay;

    /** 使用できるコマンドのメニューを開く */
    public abstract void showMenu();
    /** たたかう */
    public abstract void attack(RpgCharacter character);
    /** にげる */
    public void escape() {
        System.out.println(this.name + "は逃げ出した");
    }

そして、このクラスをインスタンス化するとき(newするとき)にデフォルトの値を設定するための

コンストラクタを定義(実装)します。引数に名前だけを与えた場合「コンストラクタ1」と、引数にすべてのプロパティ(属性)を与えた場合「コンストラクタ2」を定義しました。

    /** コンストラクタ1 */
    public RpgCharacter(String name) {
        this.name = name;
        this.hp = 10;
        this.mp = 0;
    }

    /** コンストラクタ2 */
    public RpgCharacter(String name, int sex, int age, String birthDay) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.birthDay = birthDay;
    }

RpgHeroを作る

ここでRpgHeroクラスの製造に戻ります。

ここまできたら、シンプルにRpgCharacterクラスを継承したRpgHeroクラスを作成するだけです。抽象メソッドのオーバーライドがありますが。。。

public class RpgHero extends RpgCharacter {
    /** コンストラクタ */
    public RpgHero(String name) {
        super(name);
    }
    /** コンストラクタ */
    public RpgHero(String name, int sex, int age, String birthDay) {
        super(name, sex, age, birthDay);
    }

    @Override
    public void attack(RpgCharacter monst) {
        System.out.println(this.name + "の攻撃");
        int damage = getAttackPower() - monst.getDiffencePower();
        System.out.println(monst.getName() + "へ" + damage + "のダメージ");
        monst.setHp(monst.getHp() - damage);
    }
}

継承したクラス(子クラス)から親クラスのコンストラクタ、メソッド、フィールドを呼び出したい場合は「super」をつけてやります。

同様に自分のクラス(子クラス)で保持するもの(コンストラクタ、メソッド、フィールド)を呼び出したい場合は「this」をつけてやります。

そして「@Override」というアノテーションがついているメソッドは、抽象メソッドを「オーバーライド」していることを明示的に示しています。

これと同様に、MonsterクラスとSlimeクラスも作成してしまいましょう。

Monsterクラスの製造

public class Monster extends RpgCharacter {

    /**
     * @param name
     */
    public Monster(String name) {
        super(name);
    }

    @Override
    public void attack(RpgCharacter player) {
        System.out.println(this.name + "の攻撃");
        int damage = getAttackPower() - player.getDiffencePower();
        System.out.println(player.getName() + "へ" + damage + "のダメージ");
        player.setHp(player.getHp() - damage);
    }
}

必要な属性(フィールド)はRpgCharacterクラスで持っているので、このクラスに特有な部分のみを実装します。

そして、コンストラクタに関しては、細かい情報(性別、年齢など)は必要な時にsetAge()などのメソッドで設定すれば良いので明示できに書きません。

その代わり、RpgCharacterクラスの、初期値としては「不明」「-1」などの値を設定するようにします。

int型は、参照型のようなnullという値が使えないので、-1で代用します。

つまりは、「年齢が『-1』だった場合『年齢不詳』という意味にする」というルールを追加してやれば、この問題は解決出るのでそのようにしたというところです。

スライムクラス

public class Slime extends Monster {

    /**
     * @param name
     */
    public Slime(String name) {
        super(name);
    }
}

このクラスに至っては、コンストラクタのみの実装で事足ります。

<<<前回

継承に関するページ一覧

  1. Java オブジェクト指向基礎 ~オブジェクト指向コンセプト~
  2. UMLの書き方(読み方)〜概要とクラス図〜
  3. Java クラスの継承を理解する
  4. クラスの継承〜アクセス修飾子〜
  5. クラスの継承関係を作る1
  6. クラスの継承関係を作る2

環境構築関連ページ一覧

設計関連ページ一覧

  1. 設計を始める〜1.アプリイメージ〜
  2. 設計を始める〜2.機能イメージ〜
  3. 設計を始める〜3.機能概要〜
  4. Java はじめて16 〜クラス設計から実装〜

PHP関連ページ

  1. WordPress プラグイン作成〜DBを使用する〜
  2. PHP PDO 〜MySQLにアクセスする〜
  3. PHP Ajax 〜DBに登録したデータを受信する〜
  4. Google Maps API PHP連携 〜マップ情報をDBに登録する〜
  5. PHP Image File 〜iPhoneやAndroidでの画像送受信の問題〜
  6. AngularJS Routing 〜PHPをWeb APIにする〜
  7. WordPress PHPカスタム〜根本的に見た目を変える〜
  8. WordPress PHPカスタム〜根本的に見た目を変える2〜
  9. Eclipse PHPプラグイン 〜ElipseでWordPress環境を構築〜
  10. WordPress テスト実装 〜heade-test.phpを表示〜
  11. AngularJS + PHP 〜WordPressと連携する〜
  12. AngularJS + PHP 〜AngularJSの実装〜
  13. AngularJS + PHP 〜AngularJSの実装2〜
  14. WordPress 処理解析 ~index.phpを眺める~
  15. WordPress Plugin NewStatPress ~アクセス解析プラグインAPIを使う~
  16. WordPress 処理解析 ~ログイン処理を調べる~
  17. WordPressカスタム〜アンケートボタンを追加する(設計)〜
  18. WordPressカスタム〜プラグインの作成〜
  19. WordPressカスタム〜ダッシュボードのプラグイン画面作成〜
  20. WordPressカスタム〜ダッシュボードのプラグイン画面作成2〜
  21. WordPressカスタム〜ダッシュボードのプラグイン画面作成3〜
  22. WordPress プラグイン作成〜アンケート作成プラグインを作る〜

JS関連ページ

  1. JS GoogleMaps API 〜オリジナル・データマップを作ろう〜
  2. 吹き出しにYoubetubeを埋め込む
  3. Ajax + XmlHttpRequest〜画像送信からDB登録して表示〜
  4. JS XmlHttpRequest 〜JSでの同期/非同期通信〜
  5. JS Google Maps API 〜GeoLocation 現在位置の取得〜
  6. AngularJS + PHP 〜AngularJSの実装〜
  7. AngularJS + PHP 〜AngularJSの実装2〜
  8. WordPress プラグイン作成 〜$wpdbでのSELECT〜
  9. WordPressプラグイン作成 〜HTML挿入まで完了〜
  10. WordPress プラグイン作成 〜アンケート挿入〜
  11. MAMP 起動設定 〜WordPressのテスト環境を作る〜
  12. MAMP WordPress 〜インポート時のエラー対処〜
  13. WordPress PHPカスタム〜根本的に見た目を変える2〜

数理モデル関連ページ

  1. 数学への挑戦 第二弾〜数理モデルxプログラミング〜
  2. 数学への挑戦 第二弾〜実装編:数理モデルxプログラミング〜
  3. 数学への挑戦 第二弾〜集合を使う:数理モデルxプログラミング〜
  4. 数学への挑戦 第二弾〜確率変数:数理モデルxプログラミング〜
  5. 数学への挑戦 第二弾〜期待値と分散:数理モデルxプログラミング〜
  6. 数学への挑戦 第二弾〜卒業までに彼氏ができる確率:数理モデルxプログラミング〜
  7. 数学への挑戦 第二弾〜確率変数の足し算:数理モデルxプログラミング〜
  8. 数学への挑戦 第二弾〜まとめ1:数理モデルxプログラミング〜

Windows コマンドプロンプト 事始め

コマンドプロンプト

コマンドプロンプトを使用してやるとGUIでは面倒な操作を簡単にできます。
例えば、フォルダをたくさん作成するとかファイルをいろんな場所に移動するなど。。。

しかし、コマンドプロンプトの画面が何を意味しているのか?わからないことには使うこともできません。

ちょっと使えるようになると、とても「オレってすげぇ~」と感じるかもしれません(笑)。

コマンドプロンプトの画面

初期表示したときは、ユーザーディレクトリが表示されます。
例えば、Windowsのユーザーが「Takunoji」の場合は、下のように表示されます。

C:\User\Takunoji

そして、Cドライブをネットにさらすのはよろしくないので、画像は、別のディレクトリになっていますが、イメージとしては下のようなものです。※Usersまでは共通なので。。。

そして、上の画像にある「C:\Users」というのがカレントディレクトリになります。

CDコマンドで移動することができます、そして移動したときには、カレントディレクトリがその移動先に代わります。

細かい部分は動画にて解説しました。

ますは、CDコマンドと、DIRコマンドを覚えましょう。

単純なものですので、フォルダ表示しながら解説しました。カレントディレクトリを示すC:\などは「パス」といいファイル、もしくはディレクトリの場所を示すものです。

でわでわ。。。