問題
Fizz Buzz問題というプログラミングの練習問題があります。番号に従い、以下のように返事をするという問題です。
番号i | 返事 |
---|---|
3かつ5の倍数 | FizzBuzz |
3の倍数 | Fizz |
5の倍数 | Buzz |
上記以外 | i |
実際のプログラミングでは番号iをfor文で使うと思います。例えばiが1から50まで繰り返すという具合です。この記事ではiの繰り返しについて、開始番号と終了番号を入力して、Fizz Buzzを答えるという状況を考えます。このとき、開始番号と終了番号の履歴機能を持たせるという問題を考えます。履歴は10件とします。
プログラミング
履歴機能
一例として、履歴機能にSingleton パターンを適用しました。
以下は、Fizz Buzzを答えるクラスです。staticメソッドを一つ持ちます。
FizzBuzz
package fizzbuzz;
public class FizzBuzz {
private FizzBuzz() {}
public static String replyBasedOn(int i) {
String s = null;
if (i % 3 == 0 && i % 5 == 0) {
s = "FizzBuzz";
} else if (i % 3 == 0) {
s = "Fizz";
} else if (i % 5 == 0) {
s = "Buzz";
} else {
s = String.valueOf(i);
}
return s;
}
}
以下は、履歴機能を持つクラスです。getInstance()メソッドがインスタンスを返却します。LinkedList型の変数historyStartとhistoryEndが履歴を保存するための変数です。
FizzBuzzHistory.java
package fizzbuzz;
import java.util.LinkedList;
public class FizzBuzzHistory {
private FizzBuzzHistory() {}
private static class FizzBuzzHistoryHolder {
private static final FizzBuzzHistory instance = new FizzBuzzHistory();
}
public static FizzBuzzHistory getInstance() {
return FizzBuzzHistoryHolder.instance;
}
LinkedList<Integer> historyStart = new LinkedList<>();
LinkedList<Integer> historyEnd = new LinkedList<>();
public void offer(int start, int end) {
if (historyStart.size() == 10) {
historyStart.remove();
historyEnd.remove();
}
historyStart.offer(start);
historyEnd.offer(end);
}
public Integer[] getStarts() {
return historyStart.toArray(new Integer[0]);
}
public Integer[] getEnds() {
return historyEnd.toArray(new Integer[0]);
}
}
JavaFXで動かす
実際に動かす部分はFavaFXを使いました。以下は画面です。ボタンを押すと、テキストエリアにFizzBuzzと履歴を表示します。
以下、Main.java、app.fxml、AppController.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 version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.text.Font?>
<BorderPane xmlns="http://javafx.com/javafx/" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.AppController">
<center>
<Pane prefHeight="300.0" prefWidth="300.0" BorderPane.alignment="CENTER">
<children>
<Label layoutX="85.0" layoutY="15.0" text="開始" />
<Label layoutX="85.0" layoutY="50.0" text="終了" />
<TextField fx:id="textFieldOfStart" layoutX="120.0" layoutY="10.0" promptText="開始番号" text="1" />
<TextField fx:id="textFieldOfEnd" layoutX="120.0" layoutY="50.0" promptText="終了番号" text="15" />
<Button layoutX="15.0" layoutY="10.0" mnemonicParsing="false" onAction="#output" text="出力" />
<Label layoutX="15.0" layoutY="120.0" text="FizzBuzz" />
<TextArea fx:id="textAreaOfFizzBuzz" editable="false" layoutX="66.0" layoutY="119.0" prefHeight="76.0" prefWidth="200.0">
<font>
<Font name="Monospaced Regular" size="12.0" />
</font></TextArea>
<Label layoutX="35.0" layoutY="200.0" text="履歴" />
<TextArea fx:id="textAreaOfFizzBuzzHistory" editable="false" layoutX="65.0" layoutY="200.0" prefHeight="90.0" prefWidth="200.0">
<font>
<Font name="Monospaced Regular" size="12.0" />
</font></TextArea>
<Separator layoutX="15.0" layoutY="90.0" prefHeight="15.0" prefWidth="250.0" />
</children>
</Pane>
</center>
</BorderPane>
AppController.java
package application;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import fizzbuzz.FizzBuzz;
import fizzbuzz.FizzBuzzHistory;
public class AppController {
@FXML TextField textFieldOfStart;
@FXML TextField textFieldOfEnd;
@FXML TextArea textAreaOfFizzBuzz;
@FXML TextArea textAreaOfFizzBuzzHistory;
@FXML
public void output(ActionEvent event) throws Exception {
// 値受け取り
int start, end;
try {
start = Integer.parseInt(textFieldOfStart.getText());
} catch (NumberFormatException e) {
showDialog("開始の入力が不正です。");
return ;
}
try {
end = Integer.parseInt(textFieldOfEnd.getText());
} catch (NumberFormatException e) {
showDialog("終了の入力が不正です。");
return ;
}
// 正数かチェック
if (start <= 0 || end <= 0) {
showDialog("正数を入力してください。");
return ;
}
// 相関チェック
if (end < start) {
showDialog("開始が終了より後です。");
return ;
}
// FizzBuzz出力
String lineSeparetor = System.getProperty("line.separator");
StringBuilder sb = new StringBuilder();
for (int i = start; i <= end; i++) {
sb.append(FizzBuzz.replyBasedOn(i));
sb.append(lineSeparetor);
}
textAreaOfFizzBuzz.setText(sb.toString());
// 履歴保存
FizzBuzzHistory history = FizzBuzzHistory.getInstance();
history.offer(start, end);
// 履歴出力
Integer[] historyStarts = history.getStarts();
Integer[] historyEnds = history.getEnds();
sb = new StringBuilder();
for (int i = 0; i < historyStarts.length; i++) {
sb.append(String.format("%2d: ", i+1));
sb.append("開始: ");
sb.append(historyStarts[i]);
sb.append(", 終了: ");
sb.append(historyEnds[i]);
sb.append(lineSeparetor);
}
textAreaOfFizzBuzzHistory.setText(sb.toString());
}
private void showDialog(String contentText) {
Alert alert = new Alert(AlertType.INFORMATION);
alert.getDialogPane().setHeaderText(null);
alert.getDialogPane().setContentText(contentText);
alert.showAndWait();
}
}
ボタンを押すとoutput()メソッドが動きます。そのメソッドの中でFizzBuzzHistory.getInstance()メソッドを呼び出してます。クラスの作り方から、一回目のボタン押下時のみFizzBuzzHistoryのインスタンスが生成されます。