新卒専用Advent Calendar最終日を受け持たせていただきましたhanyuといいます。
先日までの方々が軒並みなんか作ったりなんだりしている一方で、業務上ではAccessとVBAしか触れないような状態だったので、正直なところ当Advent Calendarの中でも実力不足感があります。
というわけで、技術系の記事で太刀打ちするのは早々に諦め、「全くの素人がOOPを理解するまで」という題材を、自分自身をサンプルに一説語ってみようと思います。
自分自身の経験にすぎないので、たやすく一般化できる話ではないかとは思いますが、OOPの習得に苦労している部下/後輩を持っている方の一助になれば幸いです。特に新卒の人たちは来年から先輩なのだし。
マサカリ必至な題材ですがむしろ喜んでマサカラれるぐらいの気持ちでいきます。
サンプル(筆者)の初期状態
- クラスの存在意義がわからない1
- だいたいforとifでなんとかしてた
- オブジェクト指向は「音に聞く」レベル
「Rubyの書き方」みたいなサイトで言語仕様レベルの書き方だけ学んで終わったみたいな状態です。
プログラムを書く際に、どんな時でも共通するような考え方や知識が存在しておらず、プログラム全体を設計するという発想が希薄です。
レベル1:ひよコード生産機
症状
-
当然使われるグローバル変数 → 一度変な値が入ってしまうとどこで何が原因になってるかわからずデバッグが困難になる。
-
その場の気分で決められる関数分割 → ちょっと実行順を変えようとするとあっという間に全体の影響を受け、関数の内容を頻繁に変える必要が発生してしまう
状態
俗にいう「ひよコード」2を息を吐くように書いている状態です。
この段階では「とりあえず動くようにしよう」という考えが先行しているため、「良いコード」だの、「綺麗なコード」だのという発想は弱いです。
その結果ちょっとしたプログラムでもすぐしんどい思いをしてしまうのですが、そのしんどさを、__「プログラムとはこういうもの」__と考えてゴリ押しで乗り切ってしまいます。
対処
苦しいのが当然だと思っている状態なので、__もっと楽に書ける方法がある、という発想を与える__のが最優先です。この段階の人にOOPだのなんだのかんだの言っても馬の耳に念仏です。
逆に言えば、「楽な書き方ないかなぁ」みたいなことを考えているのであればOOPをはじめとした入れ知恵を受け入れる準備があります。
とは言え、いきなりOOPよりもモジュール結合度/凝集度ぐらいから入った方が、パラダイムギャップが小さいので学習しやすいでしょう。
レベル2:模索段階
症状
-
グローバル変数を縛ってみる → __大量の引数__に咽び泣く
-
手続き型段階での様々な分割手法を調べてみる → STS分割、TR分割、前処理、主処理、後処理…
状態
特に方針もない状態で書くとつらみが深いということがわかり、なんとかしようとしている段階です。
手続き型における分割手法ならばすんなり飲み込めるものの、手続き型→OOPのパラダイムシフトはまだハードルの高い状態です。
対処
「プログラムの分割」というものが存在する、ということを理解し始めた段階ですが、OOPがその範疇にあるということはまだ理解できていないので、まだOOPはハードルが高いです。
綺麗に(手続き型で)分割したプログラムが、無秩序に書いたコードに比べて変更に強く、全体の見通しもよい、ということを実感するまでは、手続き型で適用可能な分割手法の理解を推し進めるのがベターです。
レベル3:プログラムの分割の手段としてOOPがあることに気がつく
症状
-
「互いに影響を及ぼさない小さな構成単位を組み立てる」というやり方でプログラムを書くと楽だ、ということを悟る
-
「カプセル化」によって、影響範囲が限定化できる、などの売り文句から、「OOPよさそう!」となる
状態
OOPがプログラムを扱いやすく整理整頓する技法として優れている可能性に気が付きはじめている段階。(OOPで書けるとは言ってない)
OOPというのも分割手法の一つであるという話が理解でき、__「プログラムをどのように分割すれば良いのか」__ということに意識が向くようになります。
「『オブジェクト』にわける」という考え方が、理解できないものの、適切な分割についての話を聞き入れられる状態になります。
対処
レベル2の段階でモジュール結合度の概念が理解できていれば、「カプセル化」の長所はすんなり受け入れられるはずです。個人的にはこの部分が、手続き型から順を追って説明する際に、最も理解しやすいOOPの利点だと思います。
ここで焦って_継承_だの_多態_だの_SOLID_だの_ダックタイピング_だの_Strategy_だの言いだすと情報過多になってパンクするので、まずはカプセル化が理解できるように、インスタンスを作るところからやらせましょう。
レベル4:クラスの導入
症状
-
クラスを使い始める → 「前処理クラス」などというものを爆誕せしめる。
-
クラスの分割がしっくりこなくて悩む
状態
OOPによるプログラム分割を実験している段階です。
適切な分割3はまだできませんが、それでもカプセル化の恩恵は受けるため、「クラスを使うとどうやら書きやすい」ということを身をもって知り始めます。
対処
OOPという考え方自体は導入できているため、あとは経験を積ませる必要があります。
うっかりクソひよこな実装をしてしまったがためにOOPに挫けそうになってしまっていたら、しっかり鶏になるように導いてあげましょう。もう少しで独り立ちできるはずです。
レベル5:オブジェクトを理解する
症状
-
ようやくオブジェクトを中心にプログラムを捉えられるようになる
-
このあたりから、「継承」を使いこなせるようになる
状態
クラス分割が小慣れて来る段階です。
ここまでくればOOPに則った書き方が身につき始めていると言って良いと思います。
対処
OOPという考え方自体の導入は最早必要なく、OOPで書こうとしていることを前提としたレビューをすればOKです。
レベル6:OOPのプラクティスの理解
症状
-
デザインパターンを理解し始める
-
SOLID原則だの仕様と実装の分離だのの概念を知り始める
状態
自分でOOPでの書き方を勉強できるようになっている段階です。
これ以上は積極的な支援は必要ないでしょう。
対処
OOP以外について教えてあげた方が総合力を伸ばせると思います。
結局全体としてどう指導すればいいのか
いきなりOOPを教えこんでそれをあっさり吸収するような人間ならば良いですが、なかなかそうもいかない場合は、最初は手続き型の考え方から出発するのも良いでしょう。やっぱりあの書き方は__ふつーの人間の発想に素直です__。
手続き型にせよ、OOPにせよ、(関数型にせよ)最大の関心事は__変更による影響のコントロール__です4。そのためにプログラムを__うまいこと分割します__。
その「分割」の感覚を、手続き型でつかむことができてからOOPを学ぶことで、「なんかクラス作れって言われた」みたいな、手段先行でのコーディングは減るはずです。5
「まずFactoryを作ります」「newでインスタンス化します」のような局所的な方法論も重要ですが、OOPで達成されるべきことを理解しないままであると、トンチンカンなコードを書いてしまいます。
目の前の業務を片付けるための小手先のテクニックだけでなく、長期的な役立つ「考え方」の理解をおろそかにしないようにしないようにしましょう。
-
書いてた言語はRubyです。生まれて初めて触ったのはCだったので余計クラスの必要性がわかりませんでした。 ↩
-
「イマイチなコード」を「クソコード」と呼ぶのはあんまりだという発想から生まれた言葉。「今後きれいなコードが書けるように育てるもの」というニュアンスがある。(出展) ↩
-
カプセル化がわかり始めた段階では、単一責任の原則が守られることが最も重要かと思います。 ↩
-
変更の影響のコントロールの例:クラス内の変更は外にもれないようにしよう→カプセル化/同じ動きをするものは一度の変更がすべてに行き渡るようにしよう→継承/中の動きが変わっても、クラスを使う側のコードの変更は不要にしよう→多態性 ↩
-
とりえあずクラス化しろ、と言われた結果、関係ない処理を一つにまとめてネ申を作ったり、というのは、クラスも目的を理解していないことがその原因の一つではないでしょうか。 ↩