Skip to content

Instantly share code, notes, and snippets.

@Getaji
Last active August 29, 2015 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Getaji/d6ee83adfc0fb903d5ff to your computer and use it in GitHub Desktop.
Save Getaji/d6ee83adfc0fb903d5ff to your computer and use it in GitHub Desktop.
オブジェクト指向ウェイFizzBuzz
package fizzbuzz;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
public class FizzBuzzTest {
public static Integer[] range(int start, int size) {
Integer[] values = new Integer[size];
for (int i = start; i < start + size; ++i) {
values[i - start] = i;
}
return values;
}
@Test
public void 一般的なinteger配列のFizzBuzz() {
FizzBuzz<Integer, String> fizzBuzz = new FizzBuzzStandard();
fizzBuzz.addModule(0, FizzBuzz.FIZZ_BUZZ);
fizzBuzz.addModule(1, FizzBuzz.FIZZ);
fizzBuzz.addModule(2, FizzBuzz.BUZZ);
fizzBuzz.run(range(1, 100));
assertThat(fizzBuzz.get(1), is("1"));
assertThat(fizzBuzz.get(3), is("Fizz"));
assertThat(fizzBuzz.get(5), is("Buzz"));
assertThat(fizzBuzz.get(7), is("7"));
assertThat(fizzBuzz.get(15), is("FizzBuzz"));
}
@Test
public void ナベアツ() {
FizzBuzz<Integer, String> fizzBuzz = new FizzBuzzStandard();
fizzBuzz.addModule((value, resultSet) -> {
if (value % 3 == 0 || String.valueOf(value).contains("3"))
resultSet.setReturnValue(value + "↑");
});
fizzBuzz.run(range(1, 100));
assertThat(fizzBuzz.get(1), is("1"));
assertThat(fizzBuzz.get(3), is("3↑"));
assertThat(fizzBuzz.get(5), is("5"));
assertThat(fizzBuzz.get(13), is("13↑"));
assertThat(fizzBuzz.get(15), is("15↑"));
}
}
package fizzbuzz;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
/**
* <p>FizzBuzzります。
* あらゆる型でFizzBuzzができ、戻り型も自由です。
* このクラスは抽象クラスで、FizzBuzzに必要な基本的機能を備えます。
* 利用時は、このクラスを継承して実行処理を定義してください。
*
* <p>値の検査はモジュール式となっており、値に応じて処理をするモジュールを追加する仕組みです。
* すべてのモジュールが値をスキップした場合はキャスト関数のcasterで戻り型へのキャストが行われます。
* 標準では総称型引数を用いそのままキャストする関数が定義されていますが、これは推奨されていません。
*
* <p>一般的な形式である、{@link Integer}をとり{@link String}を返す{@link FizzBuzzStandard}クラスが、
* このクラスの実装クラスとして定義されています。
*
* @author Getaji
*/
public abstract class FizzBuzz<T, R> {
/** 3で割れる場合に文字列"Fizz"を返すモジュールです。 */
public static final FizzBuzzModule<Integer, ResultSet<String>> FIZZ;
/** 5で割れる場合に文字列"Buzz"を返すモジュールです。 */
public static final FizzBuzzModule<Integer, ResultSet<String>> BUZZ;
/** 15で割れる場合に文字列"FizzBuzz"を返すモジュールです。 */
public static final FizzBuzzModule<Integer, ResultSet<String>> FIZZ_BUZZ;
/** モジュールのリスト。順番を保持する */
protected final List<FizzBuzzModule<T, ResultSet<R>>> moduleList = new LinkedList<>();
/** FizzBuzz実行結果 */
protected final Map<T, R> resultMap = new LinkedHashMap<>();
/** キャスト関数 */
protected Function<T, R> caster = (value) -> (R) value;
static {
FIZZ = (value, resultSet) -> {
if (value % 3 == 0) resultSet.setReturnValue("Fizz");
};
BUZZ = (value, resultSet) -> {
if (value % 5 == 0) resultSet.setReturnValue("Buzz");
};
FIZZ_BUZZ = (value, resultSet) -> {
if (value % 15 == 0) resultSet.setReturnValue("FizzBuzz");
};
}
/**
* モジュールを追加します。
* @param module モジュール
* @return 自身のインスタンス
*/
public FizzBuzz<T, R> addModule(FizzBuzzModule<T, ResultSet<R>> module) {
moduleList.add(module);
return this;
}
/**
* 追加位置を指定してモジュールを追加します。
* @param index 追加位置
* @param module モジュール
* @return 自身のインスタンス
*/
public FizzBuzz<T, R> addModule(int index, FizzBuzzModule<T, ResultSet<R>> module) {
moduleList.add(index, module);
return this;
}
/**
* FizzBuzzを実行します。実行内容は継承クラスに依存します。
* @param values FizzBuzzする値の配列
* @return 自身のインスタンス
*/
public abstract FizzBuzz<T, R> run(T[] values);
/**
* 値にモジュールを適用します。
* @param value 値
* @return 適用した結果
*/
protected R applyModule(T value) {
ResultSet<R> resultSet = new ResultSet<>();
for (FizzBuzzModule<T, ResultSet<R>> module : moduleList) {
module.apply(value, resultSet);
if (resultSet.isSuccess()) {
return resultSet.getReturnValue();
}
}
return caster.apply(value);
}
/**
* 値を取得します。察して
* @param value 取得する値
* @return 値
*/
public R get(T value) {
return resultMap.get(value);
}
/**
* 値のキャストに使う関数を返します。
* @return キャスター
*/
public Function<T, R> getCaster() {
return caster;
}
/**
* 値のキャストに使う関数をセットします。
* @param caster キャスター
*/
public FizzBuzz<T, R> setCaster(Function<T, R> caster) {
this.caster = caster;
return this;
}
}
package fizzbuzz;
/**
* FizzBuzz実行時に、値を検査し対応した処理をするクラスです。モジュールと呼ばれます。
* 説明飽きたからFizzBuzzクラスのstaticフィールドでも見て察して
* あとBiConsumerでよくねとか言わないこと
*
* @author Getaji
*/
@FunctionalInterface
public interface FizzBuzzModule<T, RS extends ResultSet<?>> {
/**
* 値にモジュールを適用します。
* @param value 値
* @param resultSet 結果を入れるResultSet
*/
void apply(T value, RS resultSet);
}
package fizzbuzz;
/**
* IntegerをとりStringを返す一般的なFizzBuzzランナーです。
*
* @author Getaji
*/
public class FizzBuzzStandard extends FizzBuzz<Integer, String> {
public FizzBuzzStandard() {
caster = String::valueOf;
}
@Override
public FizzBuzz<Integer, String> run(Integer[] values) {
resultMap.clear();
for (Integer value : values) {
String re = applyModule(value);
resultMap.put(value, re);
}
return this;
}
}
package fizzbuzz;
/**
* モジュールによる値の検査結果を格納するクラスです。
* 値がセットされると同時にsuccessがtrueになります。これは直接trueにすることはできません。
*
* @author Getaji
*/
public class ResultSet<R> {
private boolean success = false;
private R returnValue;
/**
* 検査が完了したか返します。
* @return 完了したか
*/
public boolean isSuccess() {
return success;
}
/**
* 結果値を返します。
* @return 結果値
*/
public R getReturnValue() {
return returnValue;
}
/**
* 結果値をセットします。
* @param returnValue 結果値
* @return 自身のインスタンス
*/
public ResultSet<R> setReturnValue(R returnValue) {
this.returnValue = returnValue;
this.success = true;
return this;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment