銀行員からのRailsエンジニア

銀行員からのRailsエンジニア

銀行員から転身したサービス作りが大好きなRailsエンジニアのブログです。個人で開発したサービスをいくつか運営しており、今も新しいサービスを開発しています。転職して日々感じていること、個人開発サービス運営のことなどを等身大で書いていきます。

当ブログではアフィリエイト広告を利用しています

AtCoderで水色になりました!【やってきたことやコンテスト中に意識していること】

こんにちは。

AtCoderで悲願の水色になりました!

2019/9 に AtCoder を始めて、9ヶ月かかりました。始めた頃は、水色は遥か先のイメージだったのでとても嬉しいです!

 

緑色になるまでにやったことAtCoder とはそもそも何なのかや、続けるモチベーションなどについてはこちらの記事に書いているので、ご興味ある方は合わせてご覧ください。

ysk-pro.hatenablog.com

 

この記事では、緑色になってから水色になるまでにやってきたことコンテスト中に意識していることを書こうと思います。

やったこと

AtCoderのコンテストに出続けること

モチベが下がったりしても、とにかく毎週末コンテストに出続けました

最近はたまに青色パフォも出るようになってきました。

コンテストの結果一覧

今までに出た全てのコンテストの結果

f:id:ysk_pro:20200531155602p:plain

レーティングの推移

維持・微減を繰り返しながら、たまにガッと上がっている感じですね。

 

解けた問題も解説動画を見ること

コンテスト後に配信される解説動画は本当に神です。

AtCoder社の snuke さんの説明は、とても分かりやすくて学ぶことが多いです。

解けた問題でも、自分とは違う解き方やアプローチを学ぶことがあるので、極力見るようにしています。

 

コンテスト中に解けなかった問題を徹底的に復習すること

解けない問題を解けるようにすることが一番レベルアップに繋がると信じているので、コンテスト中に解こうとして解けなかった問題は徹底的に復習していました。

解こうとして解けなかった問題のみで、チャレンジすら出来なかった問題まではやっていません。1つのコンテストが終わったら、解けなかった1問を翌日に数時間かけて復習している感じです。

具体的には、前述の解説動画を何度も見たり、他の方の解説ブログを読んだり(「AtCoder ABC168 E」などと検索すると分かりやすい記事がたくさん出てきます)、同じ言語で通している方のコードを読むなどして理解をして、その後に自分で何も見ずに解いて、つまずいたらまた解説に戻って、というのを繰り返しています。

かなり時間がかかることもあり苦しいですが、その分これがレートの向上に一番効果があったと思います。

 

仲間とバーチャルコンテストをやったり、お互いに解説したりすること

幸運にも勤めている会社で同じ時期に AtCoder を始めた仲間が何人かいるので、その方々と休みの日にバーチャルコンテストをやったり、難しい問題をお互いに解説し合ったりしていました。

知り合いとワイワイやるのは楽しいですよね。

自分が行なった解説をブログにまとめてみたりもしました。
ABC168 E「∙ (Bullet)」の解説 【AtCoder Beginner Contest】 - 銀行員からのRailsエンジニア

 

コンテスト中に意識していること

これまで29回コンテストに出場して多くの失敗をしてきました。そんな失敗などから学んで意識していることをまとめてみます。

とりあえず全探索を考えてみる

全探索が間に合うのであれば、全探索をするのが一番確実だと思っています。

「無理だろうな」と思ってもまずは全探索の計算量を考えて、間に合わない場合はそこから「どうやって計算量を下げようか」と考えるようにしています。

意外と全探索で間に合ってしまう問題も多く間に合わない場合も考察の取っ掛かりになるので「とりあえず全探索を考える」というのは悪くないと思います。

 

実装に入ることを焦らずに、実験を繰り返して考察に時間をかけてから実装する

考察が中途半端なまま、焦って実装に入って泥沼にハマるというのを何度か経験して学びました。

考察に時間をかけて「これだ」という解法を見つけてから実装した方がスムーズに解き終わることが多いです。

また、糸口が見つからない時はノートとペンを使ってとにかく実験(色々なケースで試すこと)をしています。頭の中で考えるよりも、手を動かして実験をしていった方が糸口が見えてくることが多かったです。

 

固定してみる・逆から考えてみる

少しテクニック的な話になってしまいますが、1つの変数を固定したり、逆から考えてみるとシンプルになる場合があるので、詰まったらよく試しています。

 

やった方がいいと分かりつつやれていないこと

この記事のメインの部分はここまでですが、反省を込めてやれていないことも書いておこうと思います。

コンテスト以外に問題を解くこと

緑色になってから、若干モチベが下がったのと趣味の個人開発により時間を割きたくなったことから、それまで続けていた過去問を解くのをやめてしまいました。

どれだけ過去問を解いたか示す画像

AtCoder Problems の過去問の解いた割合

AtCoder Scores の精進グラフ

 

アルゴリズムを学ぶこと

知り合いの青色 Coder の方に coursera の無料のアルゴリズムの講座 をおすすめいただき、やろうやろうと思いながらなかなか手がつけられていません。

定番の 蟻本 も少しだけ読みましたが、積ん読になってしまっています。

典型のアルゴリズムでも使いこなせていないものが多いので(DPを本番で一度も通したことがないです..)、そろそろ向き合わないといけないなーと思っています。

 

Ruby を卒業すること

実行速度、解説の豊富さ、ライブラリの充実などのメリットから、c++ を使った方がいいと分かりつつ、なかなか Ruby を卒業できていません。

AtCoder Problems の言語別に解いた数のページ

オールRubyでした(笑)

先日、Ruby では通せない問題(AGC44 B Joker)に初めてコンテスト本番でぶち当たって衝撃を受けたので、そろそろ c++ 覚えないとなあと思っています。

 

おわりに

できていないことも多いですが、毎週コツコツ続けることで徐々に結果が出てきて嬉しいです。

これからも無理せず、毎週のコンテストを楽しんでいこうと思います。

目指せ青色!!

サンドイッチメソッドの抽出【リファクタリング Rubyエディションまとめ7】

こんにちは。

最近 リファクタリング Rubyエディション を読みました。本書はリファクタリングの様々なパターンRubyのサンプルコードを使って解説している名著です。

実務でも活かせそうなパターンが多くあったので、いいなと思ったパターンを1つずつブログにまとめています。

今回はその第7弾で「サンドイッチメソッドの抽出」についてまとめました。
オリジナルの Ruby のサンプルコードを使って説明しています。

第6弾の「コンストラクタからファクトリメソッドへ」のまとめはこちらです。
コンストラクタからファクトリメソッドへ【リファクタリング Rubyエディションまとめ6】 - 銀行員からのRailsエンジニア

f:id:ysk_pro:20200524151911p:plain

サンドイッチメソッドの抽出 とは

使えるタイミング

ほとんど内容が同じメソッドが複数あり、違いがメソッドの途中(最初や最後ではない)にある場合に有効なリファクタリングです。

リファクタリング方法

Rubyのブロックを使用します。
差異の部分をブロック呼び出し(yield)にしてメソッドを定義します。
呼び出し元は、差異の部分をブロックで渡してメソッドを呼び出します。

サンドイッチメソッドの抽出という名前の通り、共通部分でサンドイッチしているイメージですね。

文章での説明よりサンプルコードを見た方が理解しやすいので、サンプルコードを見ていきましょう。

サンプルコード

リファクタリング

まずはリファクタリング前のコードです。

class Person
  def sunny_day_activities
    puts 'work from home'
    puts 'running' # 晴れの日はランニングする
    puts 'play games'
  end

  def rain_day_activities
    puts 'work from home'
    puts 'taking a nap' # 雨の日は外に出たくないので昼寝する
    puts 'play games'
  end
end

かなりシンプルなクラスですね。

次のように実行すると

person1 = Person.new
person1.sunny_day_activities
person1.rain_day_activities

次の結果になります。

work from home
running
play games
work from home
taking a nap
play games

sunny_day_activities メソッドと、rain_day_activities メソッドは、それぞれの 2つ目の puts 以外ほとんど同じなので共通化したいですね。

リファクタリング

「サンドイッチメソッドの抽出」を使ってリファクタリングをすると下記のようになります。

class Person
  def activities
    puts 'work from home'
    yield
    puts 'play games'
  end
end

次のように実行すると、先ほどと実行結果は同じになります。

person2 = Person.new
person2.activities { puts 'running' }
person2.activities { puts 'taking a nap' }

差異のある部分をブロックで渡し、yield でブロックを実行しています。

いい感じに共通化できましたね。

おわりに

ここまでお読みいただきありがとうございます。

リファクタリング Rubyエディションでは、このブログよりもリアルのコードで解説しており分かりやすかったので、ご興味ある方は是非ご覧ください。

次回はまた違うパターンをまとめていきます!

(追記)
第8弾として「抽象スーパークラスからモジュールへ」についてまとめました。
是非合わせてご覧ください。
ysk-pro.hatenablog.com

ABC168 E「∙ (Bullet)」の解説 【AtCoder Beginner Contest】

はじめに

難しかったですね...!コンテスト中には余裕で解けませんでした...

僕が勤めている会社の競プロの集まりで、この問題を解説する機会があり、せっかくだったので噛み砕いてブログにしてみました。

問題

f:id:ysk_pro:20200523103429p:plain

問題文と制約のスクリーンショットです。
実際の問題のページはこちらです。
E - ∙ (Bullet)

考察

全探索の計算量を考えてみる

とりあえず、全探索の計算量を考えてみます。

N匹のイワシから1匹以上を選ぶ選び方なので、それぞれのイワシについて選ぶ or 選ばないの選択をしていき、最後に全てを選ばなかったケースを引けば良さそうです。

よって、O(2^N) となります。Nの制約は2 ×10^5なので圧倒的に間に合わないですね。他の方法を考えます。

解法

A_i × A_j + B_i × B_j = 0 という数式は、i と j が混ざっていて分かりにくいので、式変形をしてみます。f(j) = g(i) の形にできると考えやすくなることが多いです。

\frac{A_j}{B_j} = - \frac{B_i}{A_i} となります。一旦、簡略化のために A, B に 0 は入らないと仮定して考察を進めます。

ここで I_i = \frac{A_i}{B_i} とすると( I はもちろん Iwashi の I です)

I_j = - \frac{1}{I_j}

とすることができます。

少しまとめると、

ということが分かります。

イワシの相性は、I の値でのみ決まるので、I の値でイワシたちをグルーピングしてみましょう。

N匹のイワシ だと考えにくいので、図のような具体的な例で考えてみます。

f:id:ysk_pro:20200523095944p:plain

イワシが6匹いて、I = 2 となるのが 2匹、I = - 1/2 が 3匹、I = 3 が1匹いるとします。

仲が悪くなってしまう I をペアにすると、ペア1、ペア2と分けられます。(ペア2は相方がいませんがペアとして考えます)

f:id:ysk_pro:20200523100136p:plain

この時、何通りの選び方ができるかを考えます。

まずペア1のみに絞って考えると、それぞれのグループ(I = 2 と I = -1/2)から同時に選べないので、

(2^2 - 1) + (2^3 - 1) + 1

となります。(カッコは分かりやすくするためにつけています)

どういうことかというと、I = 2 のイワシと I = -1/2 のイワシは同時に選べないので、I = 2 のイワシのみを選ぶ場合と I = 1/2 のイワシのみを選ぶ場合で分けて考えて、最後に足すという方針で計算します。
I = 2 のイワシのみを選ぶ場合は、それぞれのイワシを選ぶ or 選ばないなので 2^2 となり、全てのイワシを選ばないとしたケースは I = 2 のイワシを選んだことにならないので最後に 1 を引いています。これで 2^2 - 1 になります。
I = -1/2 のイワシを選ぶ場合は同様に2^3 - 1 となります。
最後に +1 をしているのは、I = 2, -1/2 のイワシをどちらも選ばなくても、ペア1からの選び方として問題にはならないからです。(I = 2 のイワシと I = -1/2 のイワシが同時に選ばれなければOK)

ペア2に対しても同様に考えることができ、

(2^1 - 1) + 1

となります。

そして、ペア1 と ペア2 は互いに独立しているので、それぞれのケース数を掛け合わせ、最後に 1匹も選ばないケースを除けば(問題の条件として1匹以上選ぶ必要があるため)全ての組み合わせを求められそうですね。

一般化すると、

  • 相性が悪い I のグループ同士をペアにして、それぞれ x 匹、y 匹含まれるとすると、組み合わせの数は (2^x -1) + (2^y + 1) - 1 で求められる
  • それぞれのペアの組み合わせの数を掛け合わせると組み合わせの総数

となります。

したがって、I を全て求める → I の値でグループ化してグループに何匹ずつ含まれるか数える → グループをペアにして組み合わせの総数を求める という感じでいけそうですね。


ただ今回、そのまま I = A/B を計算してしまうと、I は小数になるので誤差がでてしまう可能性があります。例えば、I が 0.(たくさん)1 と 0.(同じたくさん)2 は違う値なので別のグループとして扱うべきところ同じグループとしてしまう、みたいなイメージです。

小数だと誤差が出てしまいそうで怖いので、I を 小数を使わない形で表現することにします。

やり方としては、I を A と B の配列で表現します。(やり方は他にも色々あると思います)
I_i = [A_i, B_i] と表現します。

小数がなくなり誤差の問題はクリアできて嬉しいのですが、注意点がいくつかあります。

  • それぞれの値を A_iB_i最大公約数で割る必要があります。例:[1, 2] と [2, 4] は同じグループとして扱いたいためです。
  • A_i が 負の場合は、A_iB_i 両方に -1 を掛けて、A_i は必ず正の値にする必要があります。例:[-1, 2] と [1, -2] は同じグループとして扱いたいためです。


ここで、ここまで無視してきた 0 の扱いについても考えます。
0 で割ることはできないので、この式で考えます。

A_i × A_j = - B_i × B_j

  • A_iB_i のどちらも 0 のケース

どんなイワシとも相性が悪くなる最悪のイワシです。(A_jB_j に何が入ったとしても式が成り立ちます。)このタイプのイワシをクーラーボックスに入れたいときは、このタイプのイワシをただ1匹だけ入れるしかありません。よって、最悪イワシは何匹いるかを数えておき、最後に匹数を足せば良さそうです。

  • A_iB_i のどちらかだけが 0 のケース

例えば、A_i が 0 だとすると、左辺は 0 になり、B_i は 0 ではないので、B_j が 0 のイワシと相性が悪くなります。つまり、A = 0 のイワシと相性が悪いのは B = 0 のイワシです。(このとき、A = B = 0 の最悪のイワシは別管理しているので考慮する必要はないです)
B = 0 のときは I = [1, 0]、A = 0 のときは I = [0, -1] とすることで、グループ化したイワシたちと同様に考えることができます。

実装

やっと、、やっと実装です!
Ruby のコードに適宜コメントを入れています。

MOD = 1000000007
N = gets.chomp.to_i

group = Hash.new(0)
worst_iwashi_count = 0 # 最悪のイワシを管理する変数

N.times do |i|
  a, b = gets.chomp.split.map(&:to_i)

  if a == 0 && b == 0 # 最悪のイワシのケース
    worst_iwashi_count += 1
    next
  end

  if a == 0 # A = 0 のイワシのケース
    group[[0, -1]] += 1
    next
  end

  if b == 0 # B = 0 のイワシのケース
    group[[1, 0]] += 1
    next
  end

  # 最大公約数でそれぞれを割る
  gcd = a.gcd(b)
  a /= gcd
  b /= gcd

  # 正負を揃える
  if a < 0
    a *= -1
    b *= -1
  end

  group[[a, b]] += 1
end

ans = 1

group.each do |(a, b), count|
  # ペアとなるグループに含まれるイワシの数を数える
  # ペアとして計算されたグループは、重複して計算されることを防ぐために削除する
  if b < 0
    pair_count = group[[-b, a]]
    group.delete([-b, a])
  else
    pair_count = group[[b, -a]]
    group.delete([b, -a])
  end

  ans *= (2 ** count - 1) + (2 ** pair_count - 1) + 1
  ans %= MOD
end

p (ans + worst_iwashi_count - 1) % MOD

このコードを提出したAtCoderのページはこちらです。
Submission #13492364 - AtCoder Beginner Contest 168

おわりに

考慮することが多くて難しいですね...!
これをコンテスト中に通せる人をほんと尊敬します。

この問題のポイントは、

  • f(j) = g(i) の形への式変形
  • グループごとに組み合わせ数を求めること
  • 小数で誤差を出さないようにすること
  • 0の扱い

だったと思います。
同じような考え方を使う問題が出てきたときに、この問題の解法を思い出して応用できるといいですね!

初めてAtCoderの解法をブログにまとめ、口頭で解説するというのをやってみました感想としては、めちゃくちゃ時間がかかりましたが圧倒的に理解が深まりました。

また機会があればやってみようと思います。

ご指摘・ご質問等あれば、ブログのコメント・TwitterDM いただければ返信させていただきます。

ここまでお読みいただきありがとうございました。

コンストラクタからファクトリメソッドへ【リファクタリング Rubyエディションまとめ6】

こんにちは。

最近 リファクタリング Rubyエディション を読みました。本書はリファクタリングの様々なパターンRubyのサンプルコードを使って解説している名著です。

実務でも活かせそうなパターンが多くあったので、いいなと思ったパターンを1つずつブログにまとめています。

今回はその第6弾で「コンストラクタからファクトリメソッドへ」についてまとめました。
オリジナルの Ruby のサンプルコードを使って説明しています。

第5弾の「タイプコードからポリモーフィズムへ」のまとめはこちらです。
タイプコードからポリモーフィズムへ【リファクタリング Rubyエディションまとめ5】 - 銀行員からのRailsエンジニア

f:id:ysk_pro:20200520070559p:plain

サンプルコード

リファクタリング

文章での説明よりもコードを見た方が分かりやすいので、早速サンプルコードを見ていきましょう。
まずはリファクタリング前のコードです。

class Car
  def initialize(price)
    @price = price
  end

  def guarantee?
    false
  end
end

class ForeinCar < Car
  def guarantee?
    false
  end
end

class HighGradeCar < Car
  def guarantee?
    true
  end
end

Car(車)クラス、Carクラスを継承した ForeinCar(外車)クラス・HighGradeCar(高級車)クラスがあります。

imported = false
price = 200

car1 = if imported
         ForeinCar.new(price)
       elsif price > 100
         HighGradeCar.new(price)
       else
         Car.new(price)
       end
puts car1.guarantee? # -> true

作成するオブジェクトを決めるために条件分岐を使っています。また、複数箇所でオブジェクトを作成しており、同じ条件分岐が使われているとします。

このコードの問題点としては以下の点が挙げられます。

  • 条件分岐のコードが重複していること
  • 呼び出しているクラスと、オブジェクトが作られるクラス(ForeinCarなど)の結合度が強くなり、保守性が低くなっていること(例えば、ForeinCarクラスのクラス名や呼び出し方が変わると、呼び出し元のクラスも変更しなくてはいけなくなってしまいます)

リファクタリング

「コンストラクタからファクトリメソッドへ」を使ってリファクタリングしていきます。

リファクタリングはシンプルで、先ほどの Carクラスに、オブジェクト作成のためのクラスメソッドを1つ追加するのみです。

class Car
  def self.create(imported, price) # ファクトリメソッド
    if imported
      ForeinCar.new(price)
    elsif price > 100
      HighGradeCar.new(price)
    else
      new(price)
    end
  end
end

create がファクトリメソッドです。

次のように実行すると結果はリファクタリング前と同じになります。

car2 = Car.create(imported, price)
puts car2.guarantee?

オブジェクトの作成は全てファクトリメソッドで行うようにすることで、コードの重複はなくなり、クラス間の結合度も弱くすることができました。

「コンストラクタからファクトリメソッドへ」という名前は、呼び出し元からコンストラクタを呼び出していた(newしていた)ものをファクトリメソッドを呼び出すように変える、という意味のようです。

おわりに

ここまでお読みいただきありがとうございます。

リファクタリング Rubyエディションでは、この記事とは別のサンプルコードで丁寧に説明されていて分かりやすかったので、ご興味ある方は是非ご覧ください。

次回はまた違うパターンをまとめていきます!

(追記)
第7弾として「サンドイッチメソッドの抽出」についてまとめました。
是非合わせてご覧ください。
ysk-pro.hatenablog.com

タイプコードからポリモーフィズムへ【リファクタリング Rubyエディションまとめ5】

こんにちは。

最近 リファクタリング Rubyエディション を読みました。本書はリファクタリングの様々なパターンRubyのサンプルコードを使って解説している名著です。

実務でも活かせそうなパターンが多くあったので、特にいいなーと思ったパターンを1つずつブログにまとめています。

今回はその第5弾で「タイプコードからポリモーフィズムへ」についてまとめました。
オリジナルの Ruby のサンプルコードを使って説明しています。

第4弾の「nullオブジェクトの導入」のまとめはこちらです。
nullオブジェクトの導入【リファクタリング Rubyエディションまとめ4】 - 銀行員からのRailsエンジニア

f:id:ysk_pro:20200513080734p:plain

サンプルコード

リファクタリング

文章での説明よりもコードを見た方が分かりやすいので、早速サンプルコードを見ていきましょう。
まずはリファクタリング前のコードです。

class Fan
  attr_reader :type_code, :brand_level

  def initialize(type_code, brand_level)
    @type_code = type_code
    @brand_level = brand_level
  end

  def price
    case type_code
    when :ac_motor
      brand_level * 3_000
    when :dc_motor
      brand_level * 5_000
    when :no_wing
      brand_level * 7_000
    end
  end

  # priceメソッド以外にもtypeによる条件分岐が多くなっている
end

シンプルな Fan(扇風機)クラスです。
モーターのタイプを表す type_code(タイプコード)とブランドのレベルを表す brand_level(ブランドレベル)を渡してインスタンスを作成すると、価格を返す price メソッドが使用できます。

リファクタリングの名前にも入っている「タイプコード」というのは、この例の type_code のように、インスタンスの種類を表すために注入している要素のことを指します。

次のように実行します。

nomal_fan = Fan.new(:ac_motor, 1)
puts nomal_fan.price

dyson_fan = Fan.new(:no_wing, 5)
puts dyson_fan.price

balmuda_fan = Fan.new(:dc_motor, 10)
puts balmuda_fan.price

ブランドレベルは僕の主観です(笑)

結果は下記の通りとなります。

3000
35000
50000

バルミューダの扇風機は高いですね〜。

このコードの問題点は、case 文による条件分岐を行なっているところです。

今は price メソッドにしか case 文を使っていないので悪くないように見えるかもしれませんが、他のメソッドを追加する際に同じように case 文による条件分岐を作ると、case 文のところでコードの重複が発生してしまいます。

さらに、case 文が複数存在する状態で、新たなタイプコードを追加しようとした場合は、全ての case 文に修正を入れなければならなくなります

これらが重なってくるとかなり辛い状況になってしまいます。

それでは、このコードをポリモーフィズムを使ってリファクタリングしてみましょう。

リファクタリング

「タイプコードからポリモーフィズムへ」を使ってリファクタリングすると次のようになります。

class AcMotorFan
  attr_reader :brand_level

  def initialize(brand_level)
    @brand_level = brand_level
  end

  def price
    brand_level * 3_000
  end
end

class DcMotorFan
  attr_reader :brand_level

  def initialize(brand_level)
    @brand_level = brand_level
  end

  def price
    brand_level * 5_000
  end
end

class NoWindFan
  attr_reader :brand_level

  def initialize(brand_level)
    @brand_level = brand_level
  end

  def price
    brand_level * 5_000
  end
end

タイプコードそれぞれをクラスにしそれぞれのクラスで price メソッドを定義するようにしました。

次のように実行すると結果はリファクタリング前と同じになります。

nomal_fan = AcMotorFan.new(1)
puts nomal_fan.price

dyson_fan = NoWindFan.new(5)
puts dyson_fan.price

balmuda_fan = DcMotorFan.new(10)
puts balmuda_fan.price

case 文による条件分岐がなくなりましたね。

新たなメソッドを追加する際は、それぞれのクラスに新しいメソッドを書いていくだけでいいのでシンプルです。
また、タイプコードの追加もクラスを新たに作るだけで良くなりました。

「タイプコードからポリモーフィズム」という名前はそのままで分かりやすいですね。

おわりに

ここまでお読みいただきありがとうございます。

case文を使っている箇所を見つけたら「タイプコードからポリモーフィズム」が適用できないかをまず考えてみると良さそうですね。

リファクタリング Rubyエディションでは、様々なタイプのマウンテンバイクを題材に解説されていて分かりやすかったので、ご興味ある方は是非ご覧ください。

また、ポリモーフィズムについて本書に分かりやすい説明があったので引用します。

ポリモーフィズムの本質は、オブジェクトにどのようなタイプ(型)なのかを尋ね、その答に基づいてなんらかのメソッドを呼び出すようなことをせずに、単純にメソッドを呼び出すことである。呼び出されたオブジェクトは、自分の型に基づいて適切な処理を行う。
リファクタリング Rubyエディションから抜粋)

次回はまた違うパターンをまとめていきます!

(追記)
第6弾として「コンストラクタからファクトリメソッドへ」についてまとめました。
是非合わせてご覧ください。
ysk-pro.hatenablog.com

余談

最近暑くなり、扇風機選びに悩んでいたので扇風機を題材にしたサンプルコードにしてみました。
かなり悩んだ末に、個人的なブランドレベルが10の バルミューダの扇風機を購入しました!風が心地よく、コードレスが便利で大満足でした。

リモートワークでのデスク環境の紹介

リモートワークが始まって3ヶ月目に入りました。

自宅のデスク環境をコツコツ改善してきた結果、ある程度完成したのでブログにまとめてみます。

色々とこだわった結果、会社の環境より快適になってしまいました(笑)

全体像

f:id:ysk_pro:20200509094913j:plain

ポイントはカービィの卓上扇風機と子パンダのぬいぐるみです!かわいい!

それ以外にもこだわった部分があるので1つずつ紹介していきます。

デスク

電動の昇降デスクを使っています。

元々「立って作業するとかつらそう」派だったのですが、職場の昇降デスクで立って作業をしてみたら、思いのほか集中できて良かったので家でも買っちゃいました。

午後は(眠いので)基本立つようにしています。

足つぼを刺激するスリッパと合わせるのがおすすめです。

メガ押しふみっぱ ピンク(グイグイインソール)

メガ押しふみっぱ ピンク(グイグイインソール)

  • 発売日: 2016/09/05
  • メディア: ヘルスケア&ケア用品
 

チェア

定番のアーロンチェアを使っています。

1年半前に新宿の大塚家具にチェアの下見のつもりで行ったのですが、気がついたときには買っちゃってました。それぐらい座り心地が良かったです。

もともとよく腰を痛めていた僕が、アーロンチェアを使い始めてから1度も腰が痛くなっていないのは割と快挙です。20万円くらいしましたが買って良かったです。

リモートワークになる前は、僕と同じくエンジニアである妻と1台を順番に使っていましたが、2人ともリモートワークとなったのでもう1台買い足しました。

アーロンチェアは3サイズあり、僕は身長172cmで中間のBサイズ、妻は身長158cmで一番小さいAサイズがちょうど良いです。

モニター

右から 24インチ、21インチ、13インチの3画面構成です。

2画面でも十分だと思っていましたが、3画面にしてみるともう2画面には戻れないくらい快適です。

真ん中のモニターはモニターアームで固定しており、モニターの下のスペースが空いてスッキリしています。

HP 9YF44AA#ABJ HP 21.5インチワイドIPSモニター P224

HP 9YF44AA#ABJ HP 21.5インチワイドIPSモニター P224

  • メディア: エレクトロニクス
 
Herman Miller Flo Single Monitor

Herman Miller Flo Single Monitor

  • メディア: Personal Computers
 

キーボード

僕が一番気に入っているポイントがこのキーボードです!

BAROCCOの分離型キーボードを使っています。

軸は、このキーボードの前のモデルで茶軸を使っていて音が気になったので、静音赤軸にしました。

両手を机に置いた自然な姿勢でタイピングできるのがとっても楽です。あまり肩こりに悩んだことがないのですが、このキーボードのおかげなんじゃないかなーと思っています。

しかもこのキーボード、光らせることもできます...!光らせ方もパターンを変えることができて、僕は押したキーがカラフルに光る設定にしています。

分離キーボードの間にトラックパッドとノートを置くと、いい感じに収まって使いやすいので便利です。

Apple Magic Trackpad 2 - スペースグレイ

Apple Magic Trackpad 2 - スペースグレイ

  • 発売日: 2018/05/16
  • メディア: Personal Computers
 

ケーブルの収納

デスクの裏のケーブル収納部分

机の裏にケーブルトレーを取り付けて、その上に延長コードを置いています。

また、机の裏に複数箇所にケーブルクリップをつけて、ケーブルをそこを通してなるべく机の裏を這わせるようにしています。

こうすることで、正面からはほとんどケーブルが見えなくなります。

元々延長コードを下に置いていたのですが、ケーブルトレーとケーブルクリップを使うことで見た目もよくなり掃除もしやすくなりました。

macに接続しているtype-cケーブルは、macを持ち運ぶ時のために机の横にマグネットでつけられるようにしています。

デスクの横にケーブルをマグネットでつけている画像

プラス Garage 配線ケーブルトレー 幅40cm レッド 434665

プラス Garage 配線ケーブルトレー 幅40cm レッド 434665

  • 発売日: 2020/02/10
  • メディア: オフィス用品
 

おまけ(ホームジム環境)

ジムを解約して、ホームジムを作りました!

ホームジムの写真

家にあることで必ず目に入り、毎日欠かさずやるようになって筋肉量は着実にアップしている気がします。

このダンベルは優れもので、ダイヤルを回すだけで重さが変えれて最高です。

おわりに

デスク環境整えるの楽しいですよね〜!

ちなみに最初に紹介したカービィの卓上扇風機はUFOキャッチャーでめっちゃ頑張って取った非売品です!と思ってたらAmazonに普通にありました... 

これからも継続的にデスク環境の改善をしていくつもりです。(今気になっているのは、4Kディスプレイ と zoom会議用のマイク と Bluetoothスピーカーです)

オススメのグッズなどあれば是非教えてください!

(追記)

デスクをリニューアルして記事を書いたので、ぜひ合わせてご覧ください。

ysk-pro.hatenablog.com

nullオブジェクトの導入【リファクタリング Rubyエディションまとめ4】

こんにちは。

最近 リファクタリング Rubyエディション を読みました。本書は、復刻版が最近発売された名著でリファクタリングの様々なパターンRubyのサンプルコードを使って解説しています。

本書の中でいいなと思ったパターンを1つずつブログにまとめています。

今回はその第4弾で「nullオブジェクトの導入」についてまとめました。
オリジナルの Ruby のサンプルコードを使って説明しています。

第3弾の「継承から委譲へ」のまとめはこちらです。
継承から委譲へ【リファクタリング Rubyエディションまとめ3】 - 銀行員からのRailsエンジニア

f:id:ysk_pro:20200508074431p:plain

「nullオブジェクトの導入」とは

サンプルコードを見ていく前に簡単に説明します。

概要

まず「nullオブジェクト」というのは、オブジェクトが nil だった場合の処理を集めたクラスから作られたオブジェクトを指します。

オブジェクトが nil だった場合に、代わりに null オブジェクトの同名のメソッドを呼び出すことで、メソッド呼び出しするだけで処理を分けることができ、オブジェクトの nil チェックが不要になります。

※ null と nil は同じで、一般的な名称が null、Ruby内での呼び方が nil というだけです。

どんな時に使えるか

オブジェクトが nil の場合は別の値を入れる処理を繰り返している場合に有効です。

メリット

nilチェックが不要になります。

ポリモーフィズムを利用することで、よりオブジェクト指向らしいコードになります。

ポリモーフィズムの本質は、オブジェクトにどのようなタイプ(型)なのかを尋ね、その答に基づいてなんらかのメソッドを呼び出すようなことをせずに、単純にメソッドを呼び出すことである。呼び出されたオブジェクトは、自分の型に基づいて適切な処理を行う。
リファクタリング Rubyエディションから抜粋)

サンプルコード

リファクタリング

サンプルコードを見ていきましょう。
まずはリファクタリング前のコードです。

class Gym
  attr_reader :name, :trainer

  def initialize(name, trainer=nil)
    @name = name
    @trainer = trainer
  end
end

class Trainer
  attr_reader :name, :muscle_quantity

  def initialize(name, muscle_quantity)
    @name = name
    @muscle_quantity = muscle_quantity
  end
end

muscle_trainer = Trainer.new('マッチョトレーナー', 100)
gold_gym = Gym.new('ゴールドジム', muscle_trainer)
joyfit = Gym.new('ジョイフィット')

# それぞれのジムのトレーナー情報を出したい
gyms = [gold_gym, joyfit]
gyms.each do |gym|
  puts "#{gym.name}: #{gym.trainer&.name || 'トレーナー不在'} / 筋肉量: #{gym.trainer&.muscle_quantity || 0}"
  # nilチェックが多くなってつらい
end

見ての通り、筋肉量100のマッチョなトレーナーがいるゴールドジムとトレーナーが不在のJOYFITがあって、それぞれのジムのトレーナー情報を出力するプログラムです。

ゴールドジムのトレーナーは本当にマッチョでした。

上記のプログラムを実行すると、結果は下記の通りとなります。

ゴールドジム: マッチョトレーナー / 筋肉量: 100
ジョイフィット: トレーナー不在 / 筋肉量: 0

トレーナー情報を出力する部分で、nil チェックが多くてつらくなっていますね。

リファクタリング

「nullオブジェクトの導入」を使ってリファクタリングすると次のようになります。

class Gym
  attr_reader :name, :trainer

  def initialize(name, trainer=nil)
    @name = name
    @trainer = trainer || NoTrainer.new # trainerがnilの時にnullオブジェクト生成 
  end
end

class Trainer
  attr_reader :name, :muscle_quantity

  def initialize(name, muscle_quantity)
    @name = name
    @muscle_quantity = muscle_quantity
  end
end

class NoTrainer
  def name
    'トレーナー不在'
  end

  def muscle_quantity
    0
  end
end

muscle_trainer = Trainer.new('マッチョトレーナー', 100)
gold_gym = Gym.new('ゴールドジム', muscle_trainer)
joyfit = Gym.new('ジョイフィット')

# それぞれのジムのトレーナー情報を出したい
gyms = [gold_gym, joyfit]
gyms.each do |gym|
  puts "#{gym.name}: #{gym.trainer.name} / 筋肉量: #{gym.trainer.muscle_quantity}"
end

実行結果はリファクタリング前と同じになります。

Gymクラスのコンストラクタで、trainer が nil の場合に @trainer に null オブジェクト(NotTrainer.new)を入れることによって、nil チェックではなくシンプルなメソッド呼び出しで nil の場合の条件分岐が実現できていますね。

おわりに

ここまでお読みいただきありがとうございます。

Rubyのコードで nil チェックを行なっている箇所は多いと思うので、「nullオブジェクトの導入」というリファクタリングを覚えておくとリファクタリングの引き出しが増えますね。

リファクタリング Rubyエディションでは、もう少しリアルなサンプルケースを使って説明していてとても分かりやすかったので、ご興味ある方は是非ご覧ください。

次回はまた違うパターンをまとめていきます!

(追記)
第5弾として「タイプコードからポリモーフィズムへ」についてまとめました。
是非合わせてご覧ください。
ysk-pro.hatenablog.com