これの続きー。
僕の好きなコードの書き方 - Mitsuyuki.Shiiba
続:僕の好きなコードの書き方 - Mitsuyuki.Shiiba
続々:僕の好きなコードの書き方 - Mitsuyuki.Shiiba
続々々:僕の好きなコードの書き方 - Mitsuyuki.Shiiba
続々々々:僕の好きなコードの書き方 - Mitsuyuki.Shiiba
今日はここから
Coinクラス?
Coinクラスを作りたいなーと昨日思ったけど、それはやり過ぎ感あるのでやめとこう。
guava導入!
Preconditionsをstatic importして使おう。
この部分を、
public Result execute(Input in) { if (in == null) { throw new NullPointerException("In is null."); } try { return a(in.getSelected(), in.getCoins()); } catch (Exception e) { LOG.error(e); return createResult(in.getCoins(), null); } } Result a(Integer aProductId, List<Integer> aInputCoins) { if (not(isValidProductId(aProductId))) { throw new IllegalArgumentException("ProductId is not valid."); } if (not(isValidInputCoins(aInputCoins))) { throw new IllegalArgumentException("InputCoins is not valid."); } Product product = dao.findById(aProductId); if (product == null) { throw new IllegalArgumentException("Product doesn't exist."); } return b(aInputCoins, product); }
こうする。
public Result execute(Input in) { checkNotNull(in, "In is null."); try { return a(in.getSelected(), in.getCoins()); } catch (Exception e) { LOG.error(e); return createResult(in.getCoins(), null); } } Result a(Integer aProductId, List<Integer> aInputCoins) { checkArgument(isValidProductId(aProductId), "ProductId is not valid."); checkArgument(isValidInputCoins(aInputCoins), "InputCoins is not valid."); Product product = dao.findById(aProductId); if (product == null) { throw new IllegalArgumentException("Product doesn't exist."); } return b(aInputCoins, product); }
むむむ。。。
こうして見てみると、isValidProductIdとisValidInputCoinsの中を考えなおしたくなった。
こうしてみるか。
CollectionUtils使いたかったので、commons-collections4を追加。
例外のメッセージを変えた。
事前条件って感じでもないんだけど、product == null の部分もPreconditions使うことにする。
Result a(Integer aProductId, List<Integer> aInputCoins) { checkProductId(aProductId); checkInputCoins(aInputCoins); Product product = dao.findById(aProductId); checkArgument(product != null, "Product doesn't exist."); return b(aInputCoins, product); } void checkProductId(Integer aProductId) { checkNotNull(aProductId, "ProductId is null."); int productId = aProductId.intValue(); checkArgument(0 < productId && productId < 11, "ProductId is invalid."); } void checkInputCoins(List<Integer> aInputCoins) { checkNotNull(CollectionUtils.isNotEmpty(aInputCoins), "InputCoins is null or empty."); checkArgument(aInputCoins.size() < 101, "InputCoins is not valid."); }
ふむ。キモいかと思ったけど、見た感じ悪くないな。
細かいところだけど
checkArgumentで不等号使ってるんだけど、これ「0より大」より「1以上」の方が読みやすいかなと思うのでそうする。
Result a(Integer aProductId, List<Integer> aInputCoins) { checkProductId(aProductId); checkInputCoins(aInputCoins); Product product = dao.findById(aProductId); checkArgument(product != null, "Product doesn't exist."); return b(aInputCoins, product); } void checkProductId(Integer aProductId) { checkNotNull(aProductId, "ProductId must not be null."); int productId = aProductId.intValue(); checkArgument(1 <= productId && productId <= 10, "ProductId must be in the range 1-10."); } void checkInputCoins(List<Integer> aInputCoins) { checkNotNull(CollectionUtils.isNotEmpty(aInputCoins), "InputCoins must not be null nor empty."); checkArgument(aInputCoins.size() <= 100, "InputCoins size must be up to 100."); }
ここから
Result b(List<Integer> inputCoins, Product product) { List<Integer> coins2 = new ArrayList<>(); int sum = processCoins(inputCoins, coins2); if (checkPrice(sum, product)) { return createResult(inputCoins, null); } processCoins2(coins2, sum, product); return createResult(coins2, product); }
proessCoinsにinputCoinsを渡したら、coins2に使えないコインが詰められて、
戻り値としては使えるコインの合計値が返される。っぽい。
で、その合計値をチェックしてOKだったら処理して返す。
変数名から
か。まずは、bメソッドのパラメータ名と変数名の名前付けからやってみよう。
Result b(List<Integer> aInputCoins, Product aProduct) { List<Integer> returnCoins = new ArrayList<>(); int sumOfValidCoins = processCoins(aInputCoins, returnCoins); if (checkPrice(sumOfValidCoins, aProduct)) { return createResult(aInputCoins, null); } processCoins2(returnCoins, sumOfValidCoins, aProduct); return createResult(returnCoins, aProduct); }
ちょっと読みやすくなった。
processCoins
このメソッドが気持ち悪いのは、2つの処理を同時にやってるところかな。
処理的にはここでは、sumOfValidCoinsが分かれば良さそう。
で、お釣りの部分はprocessCoins2の方に任せることができるな。
まずはprocessCoinsから。ここから
int processCoins(List<Integer> inputCoins, List<Integer> coins2) { int sum = 0; for (Integer coin : inputCoins) { if (coin == 1) { sum += 0; coins2.add(coin); } else if (coin == 5) { sum += 0; coins2.add(coin); } else if (coin == 10) { sum += 10; } else if (coin == 50) { sum += 50; } else if (coin == 100) { sum += 100; } else if (coin == 500) { sum += 500; } } return sum; }
こうする。まだまだキモいけど一歩ずつ。
int calcSumOfValidCoins(List<Integer> aInputCoins) { int sumOfValidCoins = 0; for (Integer coin : aInputCoins) { if (coin == 1) { sumOfValidCoins += 0; } else if (coin == 5) { sumOfValidCoins += 0; } else if (coin == 10) { sumOfValidCoins += 10; } else if (coin == 50) { sumOfValidCoins += 50; } else if (coin == 100) { sumOfValidCoins += 100; } else if (coin == 500) { sumOfValidCoins += 500; } } return sumOfValidCoins; }
で、中を、もうちょっとマシにする。こうかな。
static final ImmutableSet<Integer> VALID_COINS = ImmutableSet.of(10, 50, 100, 500); int calcSumOfValidCoins(List<Integer> aInputCoins) { return aInputCoins.stream() .filter(coin -> VALID_COINS.contains(coin)) .mapToInt(i -> i) .sum(); }
あぁ!もう時間だ!
TODO: processCoins2でreturnCoins計算する。
今日はここまでー。ValidよりAvailableの方がいいのかなー。とか思いながら。