🤯

Rubyアンチパターン:神クラス

2022/03/14に公開

(特にRubyに限った概念ではないですが、他の言語だと微妙に状況が違うかもしれないので、ここではRubyの例に限定しています。)

機能は追加・改修する際、すでにあるクラスだけでなんとかしようとすると、特定のクラスにあらゆる機能を持たせてしまったりしかねません。そうすると「神クラス」が誕生してしまいます。

典型的な症状

  • 特定のクラスに、メソッドやインスタンス変数がやたらたくさんある状態になったりします
  • ソースコードも当然のように長かったりします
  • 機能追加・機能修正するのが困難なクラスがあったりします
  • どんな機能の修正でもなぜか特定のクラスを修正しないといけなかったりします

生じる問題

  • 壊れやすくなり、修正コストが激増します

かんたんな修正であっても、(なぜか)他の機能が壊れたりします。
テストがあれば壊れたことに気づけるかもしれませんが、気づいたところで直すには手間がかかります。テストがなければ本番投入後に壊れたことが発覚したり、ずっと後になってから実は壊れていたことに気づいたりして大変つらいことになります。
そして修正が大変ということは、下記の対処法を実践するのも大変、ということです。対応が困難なので後回しにしていると、更に症状が悪化して本当に厳しいことになります。

対処法

  • クラスを分割する

結局のところ、適切な粒度のクラスに分割するしかありません。
なお、一般論としてはクラスの重要なところから分割していくより、ちょっとしたおまけ的・小物的な機能(その機能を使うところがあまり大きくないやつ)から分割していく方が無難です。
これはクラス分割する上ではあまり効果が大きくないのですが、ある程度以上育ってしまった神クラスをまともに分割するには非常に大きなコストが必要です。安易にやると挫折します。いずれにしても小物的なものもいつかは分割しないといけないので、地道な労力を適宜投入しつつ、時間をかけて頑張るしかありません。「急がば回れ」というやつです。

  • 新しいクラスを作り、そこにメソッドを追加する

やることは上記と同じですが、すでにあるクラスを機能分割するというよりも、新しい概念(機能・役割)を導入し、そちらを使うようにする、と考えた方がより適切なこともありそうです。
具体的なやり方としては、後述の「スプラウトクラス」「ストラングラーパターン」などが知られています。

参考にしたい知見

神クラスに限らず、クラスの分割については様々な知見があります(なお、知見に詳しくなっても解決が容易になるとは限りません)。

  • スプラウトクラス、ラップクラスを導入する

『レガシーコード改善ガイド』にあるパターンです。スプラウトクラスは既存クラスから一部の機能だけを切り出して別クラスにするもので、ラップクラスは既存クラスをラップする形で新しいクラスを導入するものです。

  • ストラングラーパターンを使う

ストラングラーパターン(あるいはストラングラーアプリケーション)はマイクロサービス方面で使われるパターンですが、単一サービス内でのクラスのリファクタリングにも適用できそうです。上記のスプラウトクラス、ラップクラスの大掛かりなもの、と言えるかもしれません。
ここでは上記のクラス分割や新クラス移設の際に、えいやっと分割するのではなく、徐々に変更していくことになります。当然手間は増えますが、リスクは若干減らせるかもしれません。

  • 腐敗防止層を導入する

とりあえずは神クラスを解体するものをあきらめるけれど、せめて外部(神クラスを呼び出す側)への影響を減らしたい、という場合の対処法です。つまり、ここでは神クラスそのものを巨大な泥団子(Big ball of mud)的なものだと見なして、直接触らないようにするものです。
これがあると、腐敗防止層の手前側の世界は秩序を取り戻しやすくなります。一方で腐敗防止層そのものの修正は困難ですし、完全に隠蔽しきれないこともあるので注意が必要です。あくまで暫定的な対応として、最終的には神クラスそのものをなんとかする作業を行うべきです。
なお、「腐敗防止層」「巨大な泥団子」はDDDのパターン(後者はアンチパターン)で、『エリック・エヴァンスのドメイン駆動設計』 https://www.shoeisha.co.jp/book/detail/9784798126708 に詳しいです。

例外

WikipediaのGod Objectによれば、組込みなどのリソースの限られた状況では、神クラス・神オブジェクト的なものを使うこともあるそうです(でもちょっと意味合いが違いそうな気もする)。

合わせて読みたい

関連するアンチパターン

  • 太り過ぎモデル(Fat Model。モデルのクラスが神クラス的になるとFat Modelと化します)
  • ドメインモデル貧血症(モデルが貧血気味の場合、神クラスにその機能が実装されている場合があります)
  • 神テーブル(DB設計のアンチパターンで、1個のテーブルになんでも入っているやつです)

Discussion