このコーナーはOpt Technologiesでアドテクとかをやっているエンジニアのおじさん(自称アドテクおじさん)が、様々な技術を使って、皆さんのお悩みをスバッと解決しちゃおうという連載企画です。
記念すべき第一回のお悩みはこちら!
本日のお悩み
アドテクおじさんこんにちは。
最近、美容院に行くのがめんどくさくて、ここ4ヶ月くらい髪の毛を切っていません。
生活も仕事と自宅の往復ばかりで、一応彼氏は居るのですがこのままでも良いかなと思ってます。
自分で言うのも何ですけど、私髪の毛は綺麗な方で、少し伸びすぎた髪の毛にも愛着がなくはないんです。
ただ、彼氏からもいい加減切れよと言われていて、そろそろ切ろうか切らないか迷っています。切るのと切らないのどっちがいいでしょうか。
(Yさん / 20代 広告代理店勤務 女性)
アドテクおじさんの答え
髪は女の命とも言いますが、4ヶ月も切ってないなんて、きっと平安美女並の長くてお美しい髪をお持ちなんでしょう。
それだけ長いと、まるでスパゲッティのように絡まって、日常生活に支障を来しそうなものですが、スパゲッティコードと言えば、欠かせないのがリファクタリングですね。
リファクタリングとは
リファクタリングとは、コードにおいて、クラスや関数の挙動を変えずに内部構造などを整理することで、見通しの良いコードに変更することです。
リファクタリング―プログラムの体質改善テクニック (Object Technology Series)
- 作者: マーチンファウラー,Martin Fowler,児玉公信,平澤章,友野晶夫,梅沢真史
- 出版社/メーカー: ピアソンエデュケーション
- 発売日: 2000/05
- メディア: 単行本
- 購入: 94人 クリック: 3,091回
- この商品を含むブログ (312件) を見る
これはリファクタリングについて書かれた有名な本ですが、この本にもある通り、継続的にリファクタリングを行うことで、コードに対する修正や追加が行いやすくなり、技術的負債が溜まりにくくなります。
新装版 リファクタリング―既存のコードを安全に改善する― (OBJECT TECHNOLOGY SERIES)
- 作者: Martin Fowler,児玉公信,友野晶夫,平澤章,梅澤真史
- 出版社/メーカー: オーム社
- 発売日: 2014/07/26
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (11件) を見る
新装版も出てた。(おじさん知らなかった)
リファクタリングの例
では、実際にScalaを使ったリファクタリングの例を見てみましょう。
指定の長さまで髪の毛を切るというという関数の実装を考えてみます。
また、サンプルにならないので美容師さんが新人さんなので、 1/2
の確率でしか髪をうまく切れないことにします。
// 髪の毛と頭を表すcase class case class Hair(length: Int) case class Head(hairs: Seq[Hair]) // 髪の毛を切る関数 def cutHairsOfHead(maxLength: Int, head: Head): Head = { var newHead = head val b = new scala.util.control.Breaks b.breakable { while(true) { // まだ髪が長かったら髪の毛を切る if (newHead.hairs.map(_.length).max > maxLength) { // 1/2の確率で切る val newHairs = newHead.hairs.map(h => if (h.length > maxLength && scala.util.Random.nextBoolean) Hair(maxLength) else h) newHead = Head(newHairs) } else { b.break } } } newHead }
指定の長さまで髪の毛を切る、ということが実装できていますが、以下の点で問題があります。
- 無限ループの中で様々な処理を行っており、見通しが良くない
var
を使っている
これをリファクタリングすると、例えばこのような形になります。
// 髪の毛と頭を表すcase class case class Hair(length: Int) { // 1/2の確率で切る def cut(maxLength: Int): Hair = if (length > maxLength && scala.util.Random.nextBoolean) Hair(maxLength) else this } case class Head(hairs: Seq[Hair]) { def lengthOfHair: Int = hairs.map(_.length).max def cutHairsOnce(maxLength: Int): Head = Head(this.hairs.map(_.cut(maxLength))) } // 髪の毛を切る関数 @scala.annotation.tailrec def cutHairsOfHead(maxLength: Int, head: Head): Head = { val newHead = head.cutHairsOnce(maxLength) if (newHead.lengthOfHair <= maxLength) newHead else cutHairsOfHead(maxLength, newHead) }
こんなことをしました。
- 無限ループの中の処理を
Hair
クラスとHead
クラスに移した - 無限ループする箇所は末尾再帰に変えた
行数も少し減りましたし、var
も無くなりました。
前よりはすっきりして見やすく、さらには拡張しやすくなったのではないかと思います。
アドテクおじさんのまとめ
開発は常に技術的負債との戦いです。スケジュール上の都合や、開発者の油断によって、スパゲッティコードなどの技術的負債は簡単に生まれてしまいます。
ただ、オプトでも使っているScalaやJavaのような静的型付け言語を使えば、比較的リファクタリングは容易です。IDEがある程度勝手にやってくれたりもします。
これまで述べたような例をご参考に、Yさんの技術的負債のような髪もリファクタリングしてみてはいかがでしょうか。
これにて一件落着!
本コーナーでは、読者の皆さんからのお悩み相談を募集しております。
相談してやってもいいぞ、という寛大なお方は、下記のgoogle formからご応募ください!