Java オブジェクト指向 ~設計図と実装のイメージをつなげる~

イントロダクション

下のようなツイートをしてみたところ。。。

複数の「いいね」をもらったので、自分の知っていることを記載します。

1. 設計と実装のつながり
2. 設計(クラス図)について
3. オブジェクト指向

結論から言うと「設計図(クラス図)は実装を示している」という認識です。
しかし、「オブジェクト指向で躓く人が多い」というウワサから想像するに、この部分は多くの人がつまずく部分だと思われます。

「躓いた数なら日本一」の自信がある自分としては、以下のものが不明瞭な認識のままではなかろうかと思います。
1. 設計と実装のつながり
2. クラス同士の関係
3. クラスのインスタンス化
4. 処理の追いかけ方

サンプルとして下のようなコンソールアプリを作成しました。
※ 以前作成したのものがありました。。。

これのクラス図も作成しました。

これらを材料にして設計(クラス図)と実装のつながりを見ていこうと思います。

# 1. 設計と実装のつながり
実装をしてない状態では、上記のイメージ(クラス図)のように、詳細な処理が書かれていない状態です。
この部分は「自分で考える」必要があります。

具体的には、**単純に疑問点を明確にしていく作業**があるということです。クラス図の内容を箇条書きにすると下のようになります。

1. MainBankクラスはCalculationクラスを持っている(属性を持っている)
2. MainBankクラスはメインメソッドを持っている(操作を持っている)
3. MainBankクラスはatm()を持っている(操作を持っている)
4. Calcurationクラスはyokingaku()を持っている(属性を持っている)
5. CalcurationクラスはgetYokingaku()を持っている(操作を持っている)
6. CalcurationクラスはsetYokingaku()を持っている(操作を持っている)
6. Calcurationクラスはnyukin()を持っている(操作を持っている)
7. Calcurationクラスはvalidate()を持っている(操作を持っている)
8. Calcurationクラスはcalcurate()を持っている(操作を持っている)
9. InputCheckerクラスはvalidNyukinHikidashi()をもっている(操作を持っている)

上のような内容だと**処理の内容が**わかりません。それもそのはず、コーダー(実装者)が行うべき仕事だからです。

早い話が、自分で考えましょうということです。

上のわかっていることで**確定しているところは「属性」**です。なので属性はそのまま実装していしまいます。

#### ポイントその1
フィールド変数に関しては明確になっている、一例ですが下のような部分です。
> 1. MainBankクラスはCalculationクラスを持っている(属性を持っている)

というのは、フィールド変数にCalculationクラスをもっていて、クラス図にある「-」はアクセス修飾子が「private」ということです。

<[MainBank.java](https://github.com/ZenryokuService/PracticeJava1/blob/master/PracticeJava1/src/jp/zenryoku/apps/atm/MainBank.java)>

public class MainBank {
    /** 金銭管理クラス */
    private Calcuration cal;
}

<[Calcuration.java](https://github.com/ZenryokuService/PracticeJava1/blob/master/PracticeJava1/src/jp/zenryoku/apps/atm/Calcuration.java)>

public class Calcuration {
    /** 預金額 */
    private int yokingaku;


<[InputChecker.java](https://github.com/ZenryokuService/PracticeJava1/blob/master/PracticeJava1/src/jp/zenryoku/apps/atm/check/InputChecker.java)>

public class InputChecker {
}

## 2.設計(クラス図)について
設計(クラス図)について考えていきます。

#### 不明点のポイント
どのように実装したらよいか?というところに焦点を当てます。
不明点の残る「操作」に関してはとりあえずメソッドだけ作ってしまいます。
上のクラス図には詳細な返却値などの指定がないので、この部分も不明点 -> 自由に実装してよいところ、となるわけです。

メインメソッドは最後にします、それはこれから作成する「操作」をそろえてから、「不足する操作(メソッド)」を作ってから考えるべきだからです。※不足分を後で追加すると面倒なのです。。。

料理に例えると、先に作成する料理の、材料をそろえるようなイメージです。

まずは、不明点の洗い出しを行います。操作の内容が不明な状態なのでメソッドの一覧を作成することになります。

1. MainBank#main()に関してはメインメソッドなので最後にします。
2. MainBank#atm()は、名前からしてコーダー銀行アプリを起動するメソッドにします。
2. Calcuration#getYokingaku()は名前からしてフィールドの値を取得するメソッドにします。
3. Calcuration#setYokingaku()も名前からしてフィールドの値を設定するメソッドにします。
4. Calcuration#nyukin()メソッドは、名前からしてお金を入金するときのメソッドにします。
5. Calcuration#validate()メソッドは、名前からして入力チェックのメソッドにします。
6. Calcuration#calcurate()メソッドは、名前からして計算処理を行うメソッドにします。

ここまで作成したら、処理のイメージが湧いてくるかと思います。

大まかに、Calcurationクラスで、入金(出金)を行い、預金金額の管理も行う。というところです。

そのためには「入金(出金)(nyukin())」メソッドを用意して、計算処理のメソッド(calcurate())を用意して。。。

というような実装イメージがわくと思います。

具体的にメソッドの枠を作ってみましょう、具体的にはメソッドを空実装(中身の実装をしない)をしてみます。

### 実際の業務として実装するときは
「この部分に関しては「このように実装します。よろしいでしょうか?」などのように上長に確認しましょう。

### 操作の実装
早速、実装していきます。まずは入力(IN)と出力(OUT)を明確にします。この部分は設計の工程になります。
業務としては、この詳細な設計部分は設計書に記載されていることがほとんどですが、たまに「よろしく!」といわれることがあるので、そのときは、自分で考えます。

「このように実装します」という報告は忘れないようにしましょう。

まずは、設計図(クラス図)でもメソッドの量が多いので、CalcurationクラスのI/O(INとOUTのこと)を決定します。「空実装」を行うという意味です。しかし、ゲッターとセッターに関しては処理が決まっているので実装してしまいます。

ここまでの実装は以下になります。

public class Calcuration {
    /** 預金額 */
    private int yokingaku;

    /** コンストラクタ */
    public Calcuration() {
    }

    /** 
     * 預金金額のゲッター
     * @return the yokingaku
     */
    public int getYokingaku() {
        return yokingaku;
    }

    /**
     * 預金金額のセッター
     * @param yokingaku the yokingaku to set
     */
    public void setYokingaku(int yokingaku) {
        this.yokingaku = yokingaku;
    }

    /** 
     * 入金処理 or 引出し
     * @param input 標準入力
     * @param isNyukin
     */
    public void nyukin(Scanner input, boolean isNyukin) {
    }

    /**
     * 入力チェック処理
     * @param in
     * @return true: 入力エラー false: 入力OK!
     */
    private boolean validate(String in) {
        return false;
    }

    /**
     * 
     * @param in 数字文字
     * @param isNyukin ture: 入金処理 false: 引出し処理
     */
    private boolean calcurate(String in, boolean isNyukin) {
        return true;
    }
}

### クラス同士の関係
上のクラス図にあるクラスは以下の3つです。
1. MainBank
2. Calcuration
3. InputChecker

これらの関係性を考えると、下のような役割を持たせてやるとよい関係が築けそうです。
1. MainBank => メインメソッドを実行する
2. Calcuration => 預金金額の計算、管理を行う。
3. InputChecker => 入力チェック処理を管理する。チェック処理はここに書くということです。

この部分(クラスの関係)は、アイディアの良し悪しが入ってくる部分です、そして、プログラミングの面白いところでもあります。上記のような関係よりも良い関係があればそのように実装するべきです。

そして、それぞれのクラスの役割が決まってきたら次は、空になっているメソッドの実装を行います。

補足として、クラス関係を作るときにどう考えたら良いかわからない場合には、とりあえず1と2のクラス関係を作りプログラムを動かしてみて下さい。

書いて動かして見れば、理解出来ます。案ずるより生むが易しと言ったところです。

## 3.オブジェクト指向
ここで、頭の中を設計レベルに戻します。

具体的には、どのような処理を行うか?を考えるというところです。

以下のような、手順で考えるとよいと思います。

1. メインメソッドにコメントで処理の順番を記述する

public static void main(String[] args) {
    // 1.MainBankクラスのインスタンス化
    // 2.MainBank#atm()メソッドを呼び出す 
}


2. MainBank#atm()メソッドにコメントで処理の順番を記述する

public void atm() {
    // 1. コーダー銀行の受け付け開始文言を出力
    // 2. 無限ループを開始
    // 3. 初期画面の文言を出力
    // 4. 標準入力を受け付ける
    // 5. "bye"と入力があった場合は処理を終了する
    // 6. 入力チェックをする
    // 7. "in"と入力があった場合は入金処理を行う
    // 8. "out"と入力があった場合は出金処理を行う
}

ここで、"in" もしくは "out" が入力されたときは入金処理を呼び出します。入金時と、出金時を区別するための引数も付けます。

3. 各部品クラス(Calcuration, InputChecker)に実装コメントを書く

この様にすると、不明点を明確にすることができるのではないでしょうか?

あくまでも自分の考えた「設計」なので、もっと良い「設計」があるかもしれません。

この様に、下のクラス(オブジェクト)に役割を持たせ、必要な処理(メソッド)を実装します。処理はなるべく周りに影響が出ないように、実装しているクラスのみに影響範囲が収まるように実装します。

具体的には、実装したメソッドの中でほかのクラスの処理を呼び出すとか、用途が限定されるなどのような実装にならないようにするということです。

具体的には、計算をするメソッドを実装するのに、引数を定義せず、返り値だけ定義してしまうと、決まった計算しかできなくなります。なので、メソッドに対する汎用的な使用方法を考えた実装をしましょう。という事です。

そして、他のクラスの処理を**必ず呼び出さない**というのも不適切な判断なので、この部分はフレキシブルに実装するべきです。

つまりは、ひとつのクラスのみを使用する形で実現すると、例えば足し算をするメソッドを複数クラスに実装しなくてはなりませんが、初めから、計算クラスを作っておいて、足し算メソッドを定義しておけば、足し算処理は「計算クラスの足し算メソッド」を利用すれば、同じ処理を実装する必要がなくなります。

オブジェクト指向プログラミングでは、資源の再利用を可能にする事で無駄な作業を減らし、より良いプログラミング・ライフを楽しんで行ける様に考えます。

実装の経験(自分で実装してみるなどの経験)により、より良い方法を身に着け、より良いプログラムがかける様になります。

この様なところが**「知識」ではなく「技術」**なのです。

### クラスのインスタンス化
上記で出てきたクラスについて、考えます。
> 1. MainBank => メインメソッドを実行する
2. Calcuration => 預金金額の計算、管理を行う。
3. InputChecker => 入力チェック処理を管理する。チェック処理はここに書くということです。

ここで、特殊なのは「メインメソッド」です。これは**static**修飾子がついているので、MainBankクラスの中にあっても、定義したメソッド(メンバ・メソッド)を実行するためには、インスタンス化が必要です。
つまり「new」する必要があるということです。

インスタンス化するというのは、**PCのメモリ上に「クラス」で定義したオブジェクトを作成する**ということです。この部分は、とても抽象的なので理解に苦しみました。

自分の場合は、絵にするとわかりやすかったので絵にすると上記のようなクラス図と似たものになりました。
![](http://zenryokuservice.com/wp/wp-content/uploads/2020/11/import1-5.png)

今回の実装では、インスタンス化するクラスは1つなので、複数ある場合を顧慮しなくてよいですが、ただ一つインスタンス化していないクラスつまり、「new」していないクラスがあります。

InputCheckerクラスです。このクラスのメソッドはstatic修飾子がついていて、インスタンス化しなくてよいのです。

staticはクラスの「インスタンスに依存しない」ということなので、起動するあっぷりケーションには必ず1つです。

具体的にはメインメソッドが必ず一つです。そして、staticをつけたXXXメソッドはクラスに一つです。

例えば、上記のCalcurationクラスを複数作成した場合、預金額はCalcurationクラスのインスタンスの数だけ存在します。

public stataic void main(String[] args) {
    Calcuration calA = new Calcuration();
    Calcuration calB = new Calcuration();

    calA.nyukin(); // ここで、入金処理
    calB.nyukin(); // ここで、入金処理

    System.out.println(calA.getYokingaku());
    System.out.println(calB.getYokingaku());
 }

のように実装した場合は、calAとcalBで保持している預金金額の値が**別々に**計算されます。

逆に、預金金額の修飾子にstaticがついていた場合は、calAとcalBで保持している預金金額の値が**同じに**なります。

### 処理の追いかけ方
これは、そのまま読むしかないのですが、まずは決まっているところから記載します。
**まずはメインメソッド**から、処理が始まります。これは絶対です。
なので、メインメソッドの処理を追いかければそのまま処理を追いかけることになります。

詳細に関しては、すでに記載しているので割愛します。

以上で、設計図と実装のつながりが理解できたと思います。

如何でしょうか?

でわでわ。。。

Java ワンポイント ~コンソールをリフレッシュする~

イントロダクション

テキストRPGを作成中です。
コンソールのみで展開する仕様なので、コンソールのれふれっふ処理が必要になりました。

調べたので、アップしておきます。

Javaからコマンドを実行する

具体的には、コンソール画面(コマンドプロンプト(Win)やターミナル(Mac))でのコマンド実行をJavaプログラムから実行するということになります。

こちらのサイトを参考にすると、実行方法としては以下のようにコードを書きます。

Rumtime.getRuntime().exec(コマンド);

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

java.io.IOException: Cannot run program "cls": CreateProcess error=2, 指定されたファイルが見つかりません。
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
at java.lang.Runtime.exec(Runtime.java:621)
at java.lang.Runtime.exec(Runtime.java:451)
at java.lang.Runtime.exec(Runtime.java:348)
at jp.zenryoku.rpg.util.ConsoleUtils.clearConsole(ConsoleUtils.java:172)
at jp.zenryoku.rpg.TextRpgLogic.updateData(TextRpgLogic.java:115)
at jp.zenryoku.rpg.TextRpgGameEngine.start(TextRpgGameEngine.java:40)
at jp.zenryoku.rpg.GameMain.main(GameMain.java:16)
Caused by: java.io.IOException: CreateProcess error=2, 指定されたファイルが見つかりません。
at java.lang.ProcessImpl.create(Native Method)
at java.lang.ProcessImpl.<init(ProcessImpl.java:444)at java.lang.ProcessImpl.start(ProcessImpl.java:139)
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
... 7 more

EclipseからMainメソッドを起動した場合でした。

Fix

調べてみると下のようなコードでコンソールをクリアdできるようだ。

new ProcessBuilder("cmd", "/c", "cls").inheritIO().start().waitFor();

ただし、Eclipseではちゃんとクリアできないが、JARファイルから起動したときはちゃんとクリアされた。

関連ページ一覧

EclipseセットアップWindows版

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜

JavaFX ワンポイント 〜Fontを指定する〜

TextFIeldやTextAreaを使用するときにFontの設定をしてやると大きさなど変更できます。

設定方法は以下のようなコードです。

newWindow = new Stage();
StackPane stack = new StackPane();
textArea = new TextArea();
textArea.setFont(Font.font("MS UI Gothic", FontWeight.BLACK, 19));
textArea.setOnKeyPressed(keyEvent -> {
    KeyCode code = keyEvent.getCode();
    if (keyEvent.isShiftDown() && KeyCode.SPACE.equals(code)) {
        newWindow.close();
        isTextIn = false;
    }
});
textArea.setOnKeyReleased(keyEvent -> {
    KeyCode code = keyEvent.getCode();
    if (keyEvent.isShiftDown() && KeyCode.ENTER.equals(code)) {
        textArea.setText("");
        textArea.positionCaret(0);
    }
});
stack.getChildren().add(textArea);
Scene scene = new Scene(stack, 450, 100);
newWindow.setScene(scene);
newWindow.initModality(Modality.WINDOW_MODAL);
newWindow.initOwner(primary);
newWindow.setX(primary.getWidth() * 0.4);
newWindow.setY(primary.getHeight() * 0.8);
newWindow.show();

return newWindow;

TextAreaで実装しましたが、これはTextFieldにも使用できます。

でわでわ。。。



関連ページ一覧

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

JavaFX ワンポイント 〜新しいWindowを立ち上げる〜

JavaFXを起動するときにすでにウィンドウを1つ起動していますが、さらにウィンドウを使いたいとき。。。

今回はそんなところを「ワンポイントレッスン」的に記載したいと思います。

ちなみに、JavaFXはこんな感じのものです。余計な音が入っています。そして、文言を表示する部分でnewWindowを使用しています。

ウィンドウを新しく作る

WindowはStageです。なので、下のように実装したらできました。
参考にしたサイトはこちらです。

private Stage createNewWindow(Stage primary) {
    newWindow = new Stage();
    StackPane stack = new StackPane();
    textArea = new TextArea();
    textArea.setOnKeyPressed(keyEvent -> {
        KeyCode code = keyEvent.getCode();
        if (keyEvent.isShiftDown() && KeyCode.SPACE.equals(code)) {
            newWindow.close();
            isTextIn = false;
        }
    });
    textArea.setOnKeyReleased(keyEvent -> {
        KeyCode code = keyEvent.getCode();
        if (keyEvent.isShiftDown() && KeyCode.ENTER.equals(code)) {
            textArea.setText("");
            textArea.positionCaret(0);
        }
    });
    stack.getChildren().add(textArea);
    Scene scene = new Scene(stack, 250, 50);
    newWindow.setScene(scene);
    newWindow.initModality(Modality.WINDOW_MODAL);
    newWindow.initOwner(primary);
    newWindow.setX(primary.getWidth() * 0.4);
    newWindow.setY(primary.getHeight() * 0.8);
    newWindow.show();

    return newWindow;
}

作成したウィンドウにキーイベントを追加して見ました。

ちなみに、
簡単ですが、こんな感じで。。。

でわでわ。。。



関連ページ一覧

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

Java OpenCV エラーの対処 〜CvException /matrix.cpp:465: 〜

Exception in thread "main" CvException [org.opencv.core.CvException: cv::Exception: OpenCV(3.4.3) /opencv/modules/core/src/matrix.cpp:465: error: (-215:Assertion failed) 0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols && 0 <= roi.y && 0 <= roi.height && roi.y + roi.height <= m.rows in function 'Mat'
]
at org.opencv.core.Mat.n_submat(Native Method)
at org.opencv.core.Mat.submat(Mat.java:854)
at zenryokuservice.opencv.fx.ImageSlicerMain.loadImg(ImageSlicerMain.java:78)
at zenryokuservice.opencv.fx.ImageSlicerMain.main(ImageSlicerMain.java:107)

このエラーメッセージに、遭遇したら単純に、読み込んだファイルのサイズと読み込もうとしているサイズの指定がうまくいっていません。指定するサイズが不適切な感じです。

自分の実装したコードは下の様なものです。

URL p = this.getClass().getResource("/" );
System.out.println("*** " + p.getPath());
Mat downloadFile = Imgcodecs.imread(p.getPath());


自分の場合は上のようなコードで、リソース(ファイル)が取得できていませんでした。それゆえにエラー。

下の部分はデバッグ用に実装した部分です。取得したパスの確認をしました。

System.out.println("*** " + p.getPath())

Class#getResource()とnew File(パス)で実装する時にルートになる場所が変わるので注意が必要です。

作成したImageSlicerでイメージファイルを切り刻みます。
ソースコードは、こちらに有ります

ポイントは、ループする最中に、xPosとyPosで切り出すファイルの位置をづらしていくところです。
そして、確認用のSwingは最後に出力するイメージが表示されます。。。

private ImageIcon dstImage(String fileName, Mat downloadFile, String sufix, int xPos, int yPos, int size, int times) {
    Rect roi = new Rect(xPos, yPos, size, size);
    System.out.println("x: " + roi.x);
    System.out.println("y: " + roi.y);
    Mat target = downloadFile.submat(roi);
    MatOfByte bytes = new MatOfByte();
    Imgcodecs.imencode(".png", target, bytes);
    byte[] b = bytes.toArray();
    InputStream in = new ByteArrayInputStream(b);
    BufferedImage buf = null;
    try {
        buf = ImageIO.read(in);
        File out = new File("resources/dst/" + fileName + "_" + sufix + ".png");
        ImageIO.write(buf, "png", out);
    } catch(IOException e) {
        e.printStackTrace();
    }
    return new ImageIcon(buf);
}


javapackager コマンドエラー〜PackagerException: 無効なディレクトリ〜

イントロダクション

JavaFXをブラウザから起動しようとしてブラウザにJavaを埋め込むために下のサイトを参考にしました。そして…

https://stackoverflow.com/questions/27958019/embed-a-javafx-application-in-a-html-webpage

こっちは試してないが、載せときます。

https://docs.oracle.com/javafx/2/webview/jfxpub-webview.htm

以下のエラーにぶつかりました。

実行したこと

Exception in thread "main" com.sun.javafx.tools.packager.PackagerException: 無効なディレクトリ~/git/MathKitJavaFX/MathKitJavaFX/bin

at com.sun.javafx.tools.packager.Main.main(Main.java:217)

 実行したコマンドは以下のものです。

javapackager -createjar -outdir compiled -outfile myapp -appclass zenryokuservice.fx.tutorial.download.Xylophone -srcdir bin -v

解決

しかしこれは、mac + eclipse(自分の環境)では不要でした。

シンプルに次のコマンドで出力することができました。

javapackager -deploy -outdir deployed -outfile TestApp -width 600 -height 600 -name Xylophen -appclass zenryokuservice.fx.tutorial.download.Xylophone -v -srcdir compiled

出力結果は以下のようになりました。出力したファイルは「TestApp.html」と「TestApp.jnlp」です。

追伸:プロジェクト直下に「compiled」フォルダを作成しています。

でわでわ。。。









Java Functional Interface〜JUnitのテストでラムダ式にコード短縮〜

ポイント

テストケースの実装中に同じ処理を関数化して実行するように実装しました。

詳細

具体的には以下の赤い部分を2回書かずにFunctional Interfaceを使用するように実装しました。実装したコードはGitにアップしています。参照しているクラスは同じディレクトリに配置してあります。

/** 自分の周りをチェックするメソッドのテストケース */
@Test
public void testCheckAround() {
//// 本当はメソッド1つにつき1ケースのテストを行うが、小さなテストなので勘弁してください。。。 ////
try {
Method test = this.getPrivateMethod("checkAround", String.class);
Consumer func = str -> {

try {
test.invoke(target, str);

} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {

e.printStackTrace();

fail("アクセス違反です。");
           }
       };
//// サンプルデータ1での検証 ////
func.accept("1010010010");
// テストケース1:周囲にアイテムがあるかの判定
ClientData data = target.getClientData();
assertEquals(true, data.isItem());
// テストケース2:周囲に相手プレーヤがいる化の判定
assertEquals(false, data.isPlayer());
// テストケース3:行動できるスペースにブロックがあるかどうか
       WalkHandler handle = data.getHandler();
assertEquals(true, handle.isOkUp());
assertEquals(true, handle.isOkDown());
assertEquals(true, handle.isOkLeft());
assertEquals(true, handle.isOkRight());
//// サンプルデータ2での検証 ////
func.accept("1020020020");
// テストケース1:周囲にアイテムがあるかの判定
ClientData data2 = target.getClientData();
assertEquals(true, data2.isItem());
// テストケース2:周囲に相手プレーヤがいる化の判定
assertEquals(false, data2.isPlayer());
// テストケース3:行動できるスペースにブロックがあるかどうか
WalkHandler handle2 = data2.getHandler();
assertEquals(false, handle2.isOkUp());
assertEquals(false, handle2.isOkDown());
assertEquals(true, handle2.isOkLeft());
assertEquals(true, handle2.isOkRight());
} catch (SecurityException e) {
e.printStackTrace();
fail("セキュリティ違反です。");
} catch (IllegalArgumentException e) {
e.printStackTrace();
fail("メソッドの引数違反です。");
}
}

引数が1つで、返却値がない場合はConsumerを使用する、他の型の場合はそれぞれに対応するIFを使用するようです。参考にしたのはこちらのサイトです。

ちなみに、デバックをしている時の風景をキャプチャしてみました。

でわでわ。。。


## 関連ページ一覧

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

JUnitクラスの作成サンプル〜@Before,@Afterの使い方〜

JUnitでのテストクラス作成サンプルです。

ポイントになるのはアノテーションで以下のようになっています。

@BeforeClass: テストクラスの実行時に一度だけ起動するメソッド。
<staticメソッドである必要があります、このメソッドはクラスに1つだけ定義されるべきメソッドだからです。>
@AfterClass: テストクラスの終了時に一度だけ起動するメソッド。
<BeforeClassと同様>

@Before: テストメソッドの実行毎に呼ばれるメソッド
@After:  テストメソッドの終了時に呼ばれるメソッド

シンプルにこれだけです。あとはどのようにクラスを実装するか?になります。

サンプルで実装したクラスはこちらにアップロードしてあります。(Git)

/**
 * ND4JのNd4jクラスを学習のため、テストする
 * @author takunoji
 * 2019/05/24
 */
public class TestNd4j {
    /** テスト対象クラス */
    private static ClientManager target;

    /**
     * テストクラスの実行時に一度だけ起動するメソッド。
     * Afterは終了時に呼ばれる
     */
    @BeforeClass
    public static void init() {
         System.out.println("*** BeforeClass ***");
        target = new ClientManager();
    }

    @AfterClass
    public static void after() {
         System.out.println("*** AfterClass ***");
        target = null;
    }

    /**
     * 
     * @param  取得するメソッドの引数の型
     * @param methodName
     * @return
     */
    private  Method getPrivateMethod(String methodName, Class ... args ) {
        Method method = null;
        try {
            if (args == null) {
                method = target.getClass().getDeclaredMethod(methodName);
            } else {
                method = target.getClass().getDeclaredMethod(methodName, String.class);
            }
            // 公開レベルをテストのために変更する
            method.setAccessible(true);
        } catch (NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
            fail("メソッドの取得に失敗");
        }
        return method;
    }

    /**
     * ClientManagerのコンストラクタで、マッピング用行列を初期化する。
     * 1.初期化時に中身を4で埋める
     * 2.移動した時にMapを拡張するので配列の拡張方法も確認
     */
    @Test
    public void testCreateINDArray() {
        // INT型データの行列を作成する
        INDArray data = Nd4j.create(new int[] {3, 3});
        System.out.println("*** init zeros***");
        System.out.println(data);
        System.out.println("*** putScalar ***");
        System.out.println(data.putScalar(new int[] {2, 1}, 1.0));
        System.out.println("*** init ones ***");
        // 1の値で初期化された配列に全て3を足す
        INDArray reData = Nd4j.ones(new int[] {3, 3}).addi(3);
        System.out.println(reData);
        System.out.println(Nd4j.pad(reData, new int[] {6,  6}, Nd4j.PadMode.CONSTANT));
    }
   ・
   ・
   ・
   ・

こんな感じです、DBにアクセスしたり、テスト対象クラスが別のクラスを読んでいるときは、Mockしてスタブ実装してやります。そうすることで「単体テスト」を行うことができ、他のクラスを修正したからここのクラスも修正。。。なんていう火炎車になるような事態を避けることができます。

ちなみに、テストクラスなので初めに決めた仕様を満たすための検証を行うクラスなのでちゃんと作成すれば、仕様変更があった時などソースを修正して仕様通りに動けば仕様変更は完了になるので初めに実装しておくとあとあと楽になります。

その他、機能拡張、マイグレーションなどの対応にもテストがあれば(テストケースを通ればOKの状態なら)システムの変更なども作業工数が減り、会社の出費(主に人件費)の削減につながるのではないでしょうか?

でわでわ。。。





ND4J 行列の作成、編集 〜JUNITでのAPIテスト(学習)〜

前提

行列を使用して、マッピングをしたいと思っています。単純に自分のいる場所を中心にして、移動するたびに行列の大きさを変更して行くものです。

ちなみにAPI(Application Interface)は使用するメソッドとかクラスのことです。

ND4Jって?

Deep Learning 4J でも使用する行列計算用のフレームワーク(ライブラリ)です。細かい部分は上記のリンク先に記載してあります。(英語)

兎にも角にもコードを書いてみないと始まらないので書きます。

行列の作成

作成したコードはGitにアップしてあります

初めに3x3の行列を作成する。今後は2x3(例外が出ます)とか数値の部分を変更してやれば良い。

INDArray data = Nd4j.create(new int[] {3, 3});
System.out.println("*** init ***");
System.out.println(data);

指定した部分の値を変更する

System.out.println("*** putScalar ***");
System.out.println(data.putScalar(new int[] {2, 1}, 1.0));

行列のサイズを変更する

INDArray reData = Nd4j.ones(new int[] {3, 3});
System.out.println(reData);
System.out.println(Nd4j.pad(reData, new int[] {1,  1}, Nd4j.PadMode.CONSTANT));

<出力したログ>

*** init zeros***
[[0.00,  0.00,  0.00],  
 [0.00,  0.00,  0.00],  
 [0.00,  0.00,  0.00]]
*** putScalar ***
[[0.00,  0.00,  0.00],  
 [0.00,  0.00,  0.00],  
 [0.00,  1.00,  0.00]]
*** init ones ***
[[1.00,  1.00,  1.00],  
 [1.00,  1.00,  1.00],  
 [1.00,  1.00,  1.00]]
[[0.00,  0.00,  0.00,  0.00,  0.00],  
 [0.00,  1.00,  1.00,  1.00,  0.00],  
 [0.00,  1.00,  1.00,  1.00,  0.00],  
 [0.00,  1.00,  1.00,  1.00,  0.00],  
 [0.00,  0.00,  0.00,  0.00,  0.00]]

追記

1で初期化した後に全ての値に3を足す処理

// 1の値で初期化された配列に全て3を足す
INDArray reData = Nd4j.ones(new int[] {3, 3}).addi(3);
System.out.println(reData);

<出力結果>

[[4.00,  4.00,  4.00],  
 [4.00,  4.00,  4.00],  
 [4.00,  4.00,  4.00]]

でわでわ。。。



Java ワンポイント〜Method invoke 引数なしの場合〜

イントロダクション

JUnitでprivateメソッドのテストとか、特定のアノテーションやインターフェースのメソッドを直接呼び出したい時などにMethod.invokeが使えます。最近では言われなくなりましたが「< href="http://sp.e-words.jp/w/AOP.html">AOP」の実装で使われています。例として、特定のメソッドを呼び出す前と、後ろに何かの処理を挟み込む時に使うものです。
ウェブアプリなどではフレームワークとして実装済みのものが多いです。

前提

作成したクラスのprivateメソッドをテストしたい時、targetClass.getClass().getDeclaredMethod()」でテストしたいメソッドを取得、テスト起動!とやることができます。

早い話が

結論から言うと、インスタンスをinvoke()に渡してやれば、おっけ!

サンプルコード

JUnit使用しています。

Method method = null;
try {
method = target.getClass().getDeclaredMethod("createMap");
// 公開レベルをテストのために変更する
method.setAccessible(true);
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
boolean isError = false;
try {
System.out.println(method.invoke(target));
} catch (IllegalAccessException e) {
e.printStackTrace();
isError = true;
} catch (IllegalArgumentException e) {
e.printStackTrace();
isError = true;
} catch (InvocationTargetException e) {
e.printStackTrace();
isError = true;
} catch (Exception e) {
e.printStackTrace();
isError = true;
}finally {
if (isError) {
fail("テスト失敗");
}
}

JUNITを設定して、実行してみました。ハローワールドをやっています。

追伸、メソッドをこの様に呼び出すやり方は色々な所で使用出来ます。

一昔前は「AOP」なんて呼んでました。「横断的関心事」とかそんな言葉が残っています。
早い話が、特定のメソッドを呼び出すときの前と後ろに処理を挟み込む技術のことです。(笑)
http://sp.e-words.jp/w/AOP.html

## 関連ページ一覧

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜