Fizz Buzz問題とBuilder パターン

#問題

Fizz Buzz問題というプログラミングの練習問題があります。番号に従い、以下のように返事をするという問題です。

|  番号i        |  返事      |
|:--------------|:-----------|
| 3かつ5の倍数  |  FizzBuzz  |
| 3の倍数       |  Fizz      |
| 5の倍数       |  Buzz      |
| 上記以外      |  i         |

返事をするパターンを以下の3タイプから選択して番号iが1から50まで答えるという問題を考えました。

|  番号i        |  Japanese  |  English   | CooCluck   |
|:--------------|:-----------|:-----------|:-----------|
| 3かつ5の倍数  | フィズバス |  FizzBuzz  |  CooCluck  |
| 3の倍数       | フィズ     |  Fizz      |  Coo       |
| 5の倍数       | バス       |  Buzz      |  Cluck     |
| 上記以外      |  i         |  i         |  i         |

#プログラミング

一例として、3タイプの生成についてBuilderパターンを使いました。3タイプから選び出力するという部分はFavaFXを使い画面を作りました。

##fizzbuzzパッケージに作成したクラス

###定数クラス

定数を扱うクラスで返事のString型をまとめました。

```java:FizzBuzzConst.java
package fizzbuzz;

public class FizzBuzzConst {

    private FizzBuzzConst(){};

    public static final String FIZZ_BUZZ_JAPANESE = "フィズバス";
    public static final String FIZZ_JAPANESE      = "フィズ";
    public static final String BUZZ_JAPANESE      = "バス";

    public static final String FIZZ_BUZZ_ENGLISH = "FizzBuzz";
    public static final String FIZZ_ENGLISH      = "Fizz";
    public static final String BUZZ_ENGLISH      = "Buzz";

    public static final String FIZZ_BUZZ_COOCLUCk = "CooCluck";
    public static final String FIZZ_COO           = "Coo";
    public static final String BUZZ_CLUCK         = "Cluck";

}
```

###返事をするクラス

「3かつ5の倍数」、「3の倍数」、「5の倍数」の場合の返事をString型のフィールドとして持つクラスです。replyBasedOn()メソッドで対応する返事を返却します。フィールドのセッターは後述のBuilderクラスが使います。ゲッターは画面側から使います。

```java:FizzBuzzHuman.java
package fizzbuzz;

public class FizzBuzzHuman {

    private String Fizz;
    private String Buzz;
    private String FizzBuzz;

    public String replyBasedOn(int i) {
        if (i % 3 == 0 && i % 5 == 0) {
            return FizzBuzz;
        } else if (i % 3 == 0) {
            return Fizz;
        } else if (i % 5 == 0) {
            return Buzz;
        } else {
            return String.valueOf(i);
        }
    }

    public void setFizz(String fizz) {
        Fizz = fizz;
    }

    public void setBuzz(String buzz) {
        Buzz = buzz;
    }

    public void setFizzBuzz(String fizzBuzz) {
        FizzBuzz = fizzBuzz;
    }

    public String getFizz() {
        return Fizz;
    }

    public String getBuzz() {
        return Buzz;
    }

    public String getFizzBuzz() {
        return FizzBuzz;
    }

}
```

###Builderクラス

以下はインターフェースです。

```java:BuilderOfFizzBuzzHuman.java
package fizzbuzz;

public interface BuilderOfFizzBuzzHuman {

    void buildFizzBuzz();
    void buildFizz();
    void buildBuzz();
    FizzBuzzHuman getFizzBuzzHuman();

}
```

以下3つが実装クラスです。フィールドのString配列fizzBuzzCombに定数を格納して、buildXXX()メソッドで使用しています。3つのクラスはfizzBuzzCombに格納する定数が変わるだけになってます。

```java:BuilderOfFizzBuzzHumanJapanese.java
package fizzbuzz;

import static fizzbuzz.FizzBuzzConst.*;

public class BuilderOfFizzBuzzHumanJapanese implements BuilderOfFizzBuzzHuman {

    private FizzBuzzHuman fizzBuzzHuman;

    private static final String[] fizzBuzzComb = {
        FIZZ_BUZZ_JAPANESE, FIZZ_JAPANESE, BUZZ_JAPANESE
    };

    public BuilderOfFizzBuzzHumanJapanese() {
        this.fizzBuzzHuman = new FizzBuzzHuman();
    }

    @Override
    public void buildFizzBuzz() {
        this.fizzBuzzHuman.setFizzBuzz(fizzBuzzComb[0]);
    }

    @Override
    public void buildFizz() {
        this.fizzBuzzHuman.setFizz(fizzBuzzComb[1]);
    }

    @Override
    public void buildBuzz() {
        this.fizzBuzzHuman.setBuzz(fizzBuzzComb[2]);
    }

    @Override
    public FizzBuzzHuman getFizzBuzzHuman() {
        return this.fizzBuzzHuman;
    }

}
```

```java:BuilderOfFizzBuzzHumanEnglish.java
package fizzbuzz;

import static fizzbuzz.FizzBuzzConst.*;

public class BuilderOfFizzBuzzHumanEnglish implements BuilderOfFizzBuzzHuman {

    private FizzBuzzHuman fizzBuzzHuman;

    private static final String[] fizzBuzzComb = {
        FIZZ_BUZZ_ENGLISH, FIZZ_ENGLISH, BUZZ_ENGLISH
    };

    public BuilderOfFizzBuzzHumanEnglish() {
        this.fizzBuzzHuman = new FizzBuzzHuman();
    }

    @Override
    public void buildFizzBuzz() {
        this.fizzBuzzHuman.setFizzBuzz(fizzBuzzComb[0]);
    }

    @Override
    public void buildFizz() {
        this.fizzBuzzHuman.setFizz(fizzBuzzComb[1]);
    }

    @Override
    public void buildBuzz() {
        this.fizzBuzzHuman.setBuzz(fizzBuzzComb[2]);
    }

    @Override
    public FizzBuzzHuman getFizzBuzzHuman() {
        return this.fizzBuzzHuman;
    }

}
```

```java:BuilderOfFizzBuzzHumanCooCluck.java
package fizzbuzz;

import static fizzbuzz.FizzBuzzConst.*;

public class BuilderOfFizzBuzzHumanCooCluck implements BuilderOfFizzBuzzHuman {

    private FizzBuzzHuman fizzBuzzHuman;

    private static final String[] fizzBuzzComb = {
        FIZZ_BUZZ_COOCLUCk, FIZZ_COO, BUZZ_CLUCK
    };

    public BuilderOfFizzBuzzHumanCooCluck() {
        this.fizzBuzzHuman = new FizzBuzzHuman();
    }

    @Override
    public void buildFizzBuzz() {
        this.fizzBuzzHuman.setFizzBuzz(fizzBuzzComb[0]);
    }

    @Override
    public void buildFizz() {
        this.fizzBuzzHuman.setFizz(fizzBuzzComb[1]);
    }

    @Override
    public void buildBuzz() {
        this.fizzBuzzHuman.setBuzz(fizzBuzzComb[2]);
    }

    @Override
    public FizzBuzzHuman getFizzBuzzHuman() {
        return this.fizzBuzzHuman;
    }

}
```

###Directorクラス

以下がFizzBuzzHumanクラスのインスタンスを生成するためのクラスです。construct()メソッドがFizzBuzzHumanクラスのインスタンスを返却します。どのようなインスタンスが生成されるかはコンストラクタに渡したBuilderOfFizzBuzzHumanの実装クラスで変わります。

```java:DirectorOfFizzBuzzHuman.java
package fizzbuzz;

public class DirectorOfFizzBuzzHuman {

    private BuilderOfFizzBuzzHuman builder;

    public DirectorOfFizzBuzzHuman(BuilderOfFizzBuzzHuman builder) {
        this.builder = builder;
    }

    public FizzBuzzHuman construct() {
        this.builder.buildFizzBuzz();
        this.builder.buildFizz();
        this.builder.buildBuzz();
        return this.builder.getFizzBuzzHuman();
    }

}
```

###JavaFXのテーブル用クラス

後述する画面ではTableViewを使用してます。その際に必要になるクラスです。画面には以下のように表示しますが、その際にこのクラスを使ってます。

| 3かつ5の倍数  |  3の倍数   | 5の倍数    |
|:--------------|:-----------|:-----------|
| A             | B          | C          |


```java:FizzBuzzStatus.java
package fizzbuzz;

public class FizzBuzzStatus {

    private String fizzBuzz;
    private String fizz;
    private String buzz;

    public FizzBuzzStatus(String fizzBuzz, String fizz, String buzz) {
        super();
        this.fizzBuzz = fizzBuzz;
        this.fizz = fizz;
        this.buzz = buzz;
    }

    public String getFizzBuzz() {
        return fizzBuzz;
    }

    public String getFizz() {
        return fizz;
    }

    public String getBuzz() {
        return buzz;
    }


}
```

##JavaFXで画面を作る

以下のような画面を作りました。

![開始.png](https://qiita-image-store.s3.amazonaws.com/0/133921/1e53aef3-bec9-511f-015a-04cc81e6942b.png)

![ダイアログ.png](https://qiita-image-store.s3.amazonaws.com/0/133921/65b424a3-8a32-7300-fe82-92a8c881aadd.png)


![選択後.png](https://qiita-image-store.s3.amazonaws.com/0/133921/d7bb353a-d180-1988-64e0-1b0a41c79a61.png)



以下の動作です。

+ 3つのタイプから選択すると以下の動作をする。
    + 表に答え方のパターンが表示される。
    + テキストフィールドにFizzBuzzが表示される。
    + 上記表示処理が終わったらダイアログを出力する。

###Main.java

画面を表示するクラスです。

```java:Main.java
package application;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) {
        BorderPane root;
        try {
            root = (BorderPane)FXMLLoader.load(getClass().getResource("app.fxml"));
            Scene scene = new Scene(root);
            primaryStage.setTitle("FizzBuzz");
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
```

###app.fxml

画面の構成を定義するXMLファイルです。

```xml:app.fxml
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.String?>
<?import javafx.collections.FXCollections?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.Pane?>

<BorderPane xmlns="http://javafx.com/javafx/" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.AppController">
  <center>
    <Pane prefHeight="400.0" prefWidth="320.0">
      <children>
        <ListView fx:id="listView" layoutX="10.0" layoutY="10.0" onMouseClicked="#showStatus" prefHeight="71.0" prefWidth="300.0">
            <items>
                <FXCollections fx:factory="observableArrayList">
                    <String fx:id="statusJapanese" fx:value="Japanese" />
                    <String fx:id="statusEnglish" fx:value="English" />
                    <String fx:id="statusCooCluck" fx:value="CooCluck" />
                </FXCollections>
            </items>
        </ListView>
        <TableView fx:id="tableView" editable="false" layoutX="10.0" layoutY="100.0" prefHeight="50.0" prefWidth="300.0">
          <columns>
            <TableColumn fx:id="tableColumnFizzBuzz" prefWidth="98.0" text="3または5の倍数" />
            <TableColumn fx:id="tableColumnBuzz" prefWidth="98.0" text="5の倍数" />
            <TableColumn fx:id="tableColumnFizz" prefWidth="98.0" text="3の倍数" />
          </columns>
        </TableView>
        <TextArea fx:id="textAreaOfFizzBuzz" editable="false" layoutX="10.0" layoutY="160.0" prefHeight="220.0" prefWidth="300.0" promptText="選択ボックスからタイプをクリックしてください。" />
      </children>
    </Pane>
  </center>
</BorderPane>
```

###AppController.java

イベント処理を定義するクラスです。

```java:AppController.java
package application;

import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ListView;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;
import fizzbuzz.BuilderOfFizzBuzzHumanCooCluck;
import fizzbuzz.BuilderOfFizzBuzzHumanEnglish;
import fizzbuzz.BuilderOfFizzBuzzHumanJapanese;
import fizzbuzz.DirectorOfFizzBuzzHuman;
import fizzbuzz.FizzBuzzHuman;
import fizzbuzz.FizzBuzzStatus;

public class AppController implements javafx.fxml.Initializable {

    private FizzBuzzHuman fizzBuzzHuman;

    @FXML ListView<String> listView;

    @FXML TableView<FizzBuzzStatus> tableView;
    @FXML TableColumn<FizzBuzzStatus, String> tableColumnFizzBuzz;
    @FXML TableColumn<FizzBuzzStatus, String> tableColumnFizz;
    @FXML TableColumn<FizzBuzzStatus, String> tableColumnBuzz;

    @FXML String statusJapanese;
    @FXML String statusEnglish;
    @FXML String statusCooCluck;

    @FXML TextArea textAreaOfFizzBuzz;

    @Override
    public void initialize(java.net.URL location, java.util.ResourceBundle resources) {

        tableColumnFizzBuzz.setCellValueFactory(new PropertyValueFactory<FizzBuzzStatus, String>("fizzBuzz"));
        tableColumnFizz.setCellValueFactory(new PropertyValueFactory<FizzBuzzStatus, String>("fizz"));
        tableColumnBuzz.setCellValueFactory(new PropertyValueFactory<FizzBuzzStatus, String>("buzz"));

    }

    @FXML
    public void showStatus(MouseEvent event){

        String str = listView.getSelectionModel().getSelectedItem();

        if (str == null) return;

        if (str.equals(statusJapanese)) {
            fizzBuzzHuman = new DirectorOfFizzBuzzHuman(new BuilderOfFizzBuzzHumanJapanese()).construct();
        } else if (str.equals(statusEnglish)) {
            fizzBuzzHuman = new DirectorOfFizzBuzzHuman(new BuilderOfFizzBuzzHumanEnglish()).construct();
        } else if (str.equals(statusCooCluck)) {
            fizzBuzzHuman = new DirectorOfFizzBuzzHuman(new BuilderOfFizzBuzzHumanCooCluck()).construct();
        }

        // ステータスを表示
        ObservableList<FizzBuzzStatus> list = tableView.getItems();
        list.clear();
        list.add(new FizzBuzzStatus(fizzBuzzHuman.getFizzBuzz(), fizzBuzzHuman.getFizz(), fizzBuzzHuman.getBuzz()));

        // 出力を空にする
        textAreaOfFizzBuzz.setText(null);

        // FizzBuzz出力
        String lineSeparetor = System.getProperty("line.separator");
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= 50; i++) {
            sb.append(fizzBuzzHuman.replyBasedOn(i));
            sb.append(lineSeparetor);
        }
        textAreaOfFizzBuzz.setText(sb.toString());

        // 完了通知
        showDialog("出力が完了しました。");

    }

    private void showDialog(String contentText) {

        Alert alert = new Alert(AlertType.INFORMATION);
        alert.getDialogPane().setHeaderText(null);
        alert.getDialogPane().setContentText(contentText);
        alert.showAndWait();

    }

}
```

showStatus(MouseEvent event)メソッドはListViewのonMouseClickedで指定したメソッドです。マウスでクリックされたときに呼び出されます。選択項目がない場合も呼び出されるため、nullチェックをしています。

javafx.fxml.Initializableはコントローラ初期化インタフェースです。ここで行っている処理はTableColumnそれぞれとFizzBuzzStatusのプロパティの紐づけです。この処理がないとTableViewへの追加処理が上手くいきません。