長い処理を関数で分けたら見やすくなった

# 「長い処理」が現れた
長ーい処理、何やってるのか見づらいですよね。
書いてる時は隅々まで理解してるけど、数ヶ月後に処理追加とかしようとすると、なにやってるか理解するまで時間かかりますよね。
書いてる本人がそうなんだから、プルリクエストのレビューアーは、余計わかりませんよね。

## 「長い処理」例

数年前に作ったコードのmain() だけ抜粋してコメント行を削除してみた。うーん、何やってるのか良く読まないとわからないですね。

```python
def main():
    pygame.init()
    screen = pygame.display.set_mode(WD_RECT.size)
    pygame.display.set_caption("hogehoge")

    gr_draw = pygame.sprite.RenderUpdates()
    gr_collision = pygame.sprite.Group()

    Gun.containers = gr_draw
    Bullet.containers = gr_draw
    Disk.containers = gr_draw, gr_collision

    gun = Gun("gun.png")
    bullet = Bullet("bullet.png", gun)
    disk = Disk("disk.png")
    score = Score()
    hit_score = Hit_score()
    miss = Miss()
    gameov = Gameover()

    clock = pygame.time.Clock()

    while True:
        clock.tick(60)
        screen.fill((0, 20, 0))

        if disk.remain <= 0:
            gameov.draw(screen, score.score)
        else :
            gr_draw.update()
            gr_collision.update()
            
            disks_collided = pygame.sprite.spritecollide(bullet, gr_collision, True)
            if disks_collided:
                for bkdisk in disks_collided:
                    inc_score = (210 - bkdisk.rect.centery) * 10
                    score.add_score(inc_score)

                    hit_score.hit(inc_score, bkdisk.rect.centerx, bkdisk.rect.centery)

                    bkdisk.collided()
                    bkdisk.add(gr_collision)


        gr_draw.draw(screen)
        gr_collision.draw(screen)

        score.draw(screen)
        hit_score.draw(screen)
        miss.draw(screen, 10 - disk.remain)

        pygame.display.update()
          

        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.quit()
                sys.exit()
            elif event.type == KEYDOWN and event.key == K_ESCAPE:
                pygame.quit()
                sys.exit()
            elif event.type == KEYDOWN and event.key == K_RETURN:
                if disk.remain <= 0:
                    disk.remain = 10
                    score.score = 0
```

# まとまった処理単位で関数化する

例えば、`main()` の始めの部分について、初期化してインスタンスを生成していますので、まとめて関数化してしまいましょう。
```python
def initializeAndCreateInstance(pygame)
    pygame.init()
    screen = pygame.display.set_mode(WD_RECT.size)
    pygame.display.set_caption("hogehoge")

    gr_draw = pygame.sprite.RenderUpdates()
    gr_collision = pygame.sprite.Group()

    Gun.containers = gr_draw
    Bullet.containers = gr_draw
    Disk.containers = gr_draw, gr_collision

    gun = Gun("gun.png")
    bullet = Bullet("bullet.png", gun)
    disk = Disk("disk.png")
    score = Score()
    hit_score = Hit_score()
    miss = Miss()
    gameov = Gameover()

    clock = pygame.time.Clock()

    return (
        gr_draw,
        gr_collision,
        gun, 
        bullet, 
        disk, 
        score, 
        hit_score, 
        miss, 
        gameov, 
        clock
        )
```

こんな風にして関数化してゆくと、`main()` は下記のようになります。[^1]

```python
def main():
    (gr_draw,
     gr_collision,
     screen, 
     gun, 
     bullet, 
     disk, 
     score, 
     hit_score, 
     miss, 
     gameov, 
     clock) = initializeAndCreateInstance(pygame)

    while True:
        setFrameRateFPS(60)
        clearScreen(screen)

        if isGameOver(disk):
            displayGameover(screen, score)
        else :
            updateSpriteCharactersOnScreen()
            CheckCollision(bullet, gr_collision, hit_score)

        drawScreen(screen, score, hit_score, disk)          

        for event in pygame.event.get():
            if event.type == QUIT:
                quitGame()
            elif event.type == KEYDOWN and event.key == K_ESCAPE:
                quitGame()
            elif event.type == KEYDOWN and event.key == K_RETURN:
                if isGameOver(disk):
                    restartGame(disk, score)
```

分かりやすくなりました。
変数名をもっと分かりやすく工夫したり、最後のイベントループ処理も関数化するとより見やすくなります。

- **まとまった処理単位で関数化する**。

- **関数名、変数名は何を行っているか想像できる名称にする**。例えば`start()`では何を開始するのかわからない。`initializeStart()`なら「初期化開始」と想像できる 

- **1つの関数は、1画面で収まるようにする**。スクロールしないと全体を見ることができない関数は処理が長すぎると思ってください。

### 疑問：コメントたくさん入れれば分かりやすくなるよね？
確かに各処理毎に説明コメントを入れれば分かりやすくなりますね。しかし、賛否両論あるものの、下記理由で私は説明コメント入れない派です。

- 処理追加等で、コードに変更があった場合、説明コメントもメンテナンスしないといけない。このメンテナンスは「後でやろう」なんて思っているうちに忘れ去られる傾向にある
- 古い情報のコメントが残ってしまうと、後の作業時にそのコメントにより作業者が困惑し、真意を確かめるのに無駄な工数がかかる
- コメントが無いとコメント修正工数がかからない

### 疑問：いきなり関数分けしたコードなんて書けないよね？
その通りです。
このような関数分けは、一旦長い処理でも動作するコードを作成してから、リファクタリングとして実施するのが良いです。
慣れてくると、ある程度関数分けしてコード作成はできるようになりますが、完全ではないと思います。コード書いてみないと分からない部分もありますからね。

# まとめ
新人教育であまりやらないけど、知ってて欲しい事を少々書いてみました。
見やすいコードにすることで、レビューアーの理解時間の短縮や、後々の機能追加時間短縮が期待できます。コードを作成したら、ちょっと一手間かけて関数分けのリファクタリングをしてみませんか？

[^1]: 説明用として記載したので、もしかしたら不備あるかもです。その時はごめんなさい。