"else if"を書くよりも先に...

まえおき

最近、仕事をしていて「つまらない、もうやめたい」って思ったときってどんなときだったっけ?と振り返ってみた。

モチベーションが下がりまくりな仕事の一因として、「パターンが多い」ってのがあると思った。

function handleWebHook(event) {
  if (event.type == "Liked") {
    if (event.source == "Article") {
      handleArticleLiked(event.data);
    }
  } else if (event.type == "Comment") {
    if (typeof event.data["bot_id"] != "undefined") {
      handleBotMessage(event.data);
    }
  } else if ( ... ) {
    ...

重厚長大なif文は、テスト書くときにも死ぬほどパターンを用意しないといけなくて、とにかくストレスがたまる。

こんな感じのコードが300行くらい続いたら、確実に集中力が切れる。てかキレる。

"else if" よりも、クラス分割?

であれば、分割すればいいやん?というのは自然な発想。

ここでいきなりPythonっぽいサンプルコードになるけど、

class LikeHandler(BaseHandler):
  def shouldHandle(event):
    return event.type == "Liked"

  def handleEvent(event):
    if event.source == "Article":
      handleArticle(event.data)


class CommentHandler(BaseHandler):
  def shouldHandle(event):
    return event.type == "Comment"

  def handleEvent(event):
    if "bot_id" in event.data:
      handleBotMessage(event.data)

...

eventHandlers = [
  LikeHandler,
  CommentHandler,
   ...
];

def handleWebHook(event):
  for handler in eventHandlers:
    if handler.shouldHandle(event):
      handler.handleEvent(event)

Pythonに限った話ではなくて、JavaでもRubyでもだいたいこんな感じで分割するのはぱっと思いつく。分割することで、テストもクラスに応じて1つずつ書いていけるし、ストレス値は一気に減る。

でも・・・

  • 毎度 def shouldHandle とか event.type== とか def handleEvent とか書かないといけないのってだるいよね
  • def handleEvent って結局どういう処理するのかって、ソースのコメント書かないと、メソッド名ではぱっと読めないよね
  • 多くのhandlerは event.typeで判定するんだろうけど、ほんとにそうなの?ってのが全部のhandlerみないとわからない

というところが今度は地味なモチベーション低下要因になってくる。

より "else if" の「判断ポイント」を明確にする書き方

Pythonbottle とかFlask っていうAPIサーバを書くライブラリをみて、ふと思った。

かりに

router = Router()

@router.on('Liked')
def handleLike(event):
  likeRouter.route(event.source, event)

@router.on("Comment"):
def handleComment(event):
  if "bot_id" in event.data:
    ...

 (中略)

def handleWebHook(event):
  router.route(event.type, event)

みたいに書くことができたらどうなるだろう?と。

毎回 event.type== xxx とか should_handle(event): return xxx とかを書く手間もなければ、判断基準(event.typeで判定しているという事実)がブレることもない。

直感的に「これだ!」とおもったので、Pythonで適当にライブラリを作ってみた。

github.com

Pythonじゃなかったらどう書く?

JSならこんな感じ?

class Route {
  constructor() {
    this.routes = {}
  }

  on(key, target) {
    this[key] = target;
  }

  route(key, ...args) {
    if (typeof this[key] == 'function') {
      this[key](...args);
    }
  }
}

r = new Route()

r.on("Liked", (event) => {
  ...
})

r.on("Comment", (event) => {
  ...
})


///////////

r.route(e.type, e);

結論?

else if を30個くらい書いてて吐き気がするところは、積極的に分割しよう。

Routerっぽいものを実装すると見通しが良くなるかもしれない。