見出し画像

プログラマが身につけるべき抽象化能力

プログラムには良い書き方と悪い書き方がある。読みやすいプログラムだとかエラー処理がきっちり書かれているとか、もちろんそれらも非常に重要なことではある。しかしその前にプログラミングロジックとして最適かどうかが最も重要だ。そこで一番最初に必要なのが抽象化能力と僕は考えている。また、これを身につけるとプログラム以外でも様々な場面で役に立つだろう。地頭が一段階良くなるみたいなものだ。ぜひ日々意識して身につけてほしい。

対象読者

本来は人を選ばない一般論ではあるが、説明のためにプログラムやシステムの例を使っているのでプログラムを勉強している人。エンジニアとして仕事を始めたけどいまいち評価されていない人。プログラム関連の知識を憶える以外にどうやって成長したらいいかがわからない人に向けた記事だ。抽象化能力ってどういうこと?と思っているあなただ。

抽象化とは

まずはこのRubyのプログラムを見てほしい。0がグー、1がチョキ、2がパーということで、何をしているかは見ればわかるだろう。

//0がグー
//1がチョキ
//2がパー

if userA == 0 then
   if userB == 0 then
       print "あいこ"
   elsif userB == 1 then
       print "Aの勝ち"
   elsif userB == 2 then
       print "Bの勝ち"
   end
elsif userA == 1 then
   if userB == 0 then
       print "Bの勝ち"
   elsif userB == 1 then
       print "あいこ"
   elsif userB == 2 then
       print "Aの勝ち"
   end
elsif userA == 2 then
   if userB == 0 then
       print "Aの勝ち"
   elsif userB == 1 then
       print "Bの勝ち"
   elsif userB == 2 then
       print "あいこ"
   end
end

そう。userAとuserBがジャンケンで手を出したので、どちらが勝ちかまたはあいこか結果判定して画面表示しているプログラムだ。このプログラム、実は次の2行で全く同じ挙動をする。ちなみにRuby以外の言語では正しく動かないケースもあるがそこは今回は省略する。

result = ["あいこ", "Bの勝ち", "Aの勝ち"]
print result[ (userA - userB) % 3 ]

つまりuserAとuserBの2つの値の差に注目したアプローチだ。同じ手を出した時は引き算の結果は当然0になる。なのでresult[0]はあいこ。差が1のときはuserAがチョキに対してuserBがグーのようなケースだからuserBの勝ち、差が2のときはuserAの勝ちになるためresultにそうなるように結果文字列を入れている。

ここで気になるのが引き算の結果がマイナスになるようなケースだが、そのような場合も上手く循環して結果が正しく出るように割り算の余りを出す%を利用しているわけだが、少し数学の知識が必要になるためここでは省略したい。

つまり言いたいことは、わざわざ9パターンもの条件分岐を使わなくても、1つのルールで処理できるということ。そしてこういうことがプログラムの世界では頻繁に必要になる。求められた結果通りに動作するように、それを逐一プログラミングするのではなく、抽象化したルールで捉え直し、1つのシンプルなロジックに落とし込めればそうしたほうがいい。

前者のプログラムにバグがあった場合、20行以上のプログラムからどこが間違えているのか見つけ出して修正しなければならない。しかし後者のプログラムはたった2行なのでバグを見つけるのも容易いし、そもそも2行のプログラムにバグを埋め込む可能性も小さい。

このように抽象化能力は、プログラマの成果物に大きな差をつけるほぼ必須の能力である。初学者にとって最初のうちは知識を覚えることが能力を高める方法であったが、一通りの言語仕様やフレームワークの使い方を覚えたら次はいかに上手く作れるようになるかだ。抽象化能力を鍛えるためにどういう考え方をすればいいのか書いていく。

【抽象化のコツ1】  1つの大きなルールでまとめる

うちの新人プログラマが以下のようなコードを書いていた。

if from_date && to_date then
   print from_date + ' 〜 ' + to_date # => 2021/09/18 〜 2021/09/30
elsif !from_date && to_date then
   print ' 〜 ' + to_date # => 〜 2021/09/30
elsif from_date && !to_date then
   print from_date + ' 〜 ' # => 2021/09/18 〜 
end

開始日(from_date)と終了日(to_date)に値が入っているかで以下のように表記方法を変えたいというプログラムだ。

・開始日、終了日の両方値がある場合 => 2021/09/18 〜 2021/09/30
・開始日がなく終了日のみある場合 =>  〜 2021/09/30
・開始日のみあり終了日がない場合 => 2021/09/18 〜

この条件分岐は大きな一つのルールでまとめ上げられる。つまりは以下のように開始日と終了日は常に表示され、値がない場合のみ空文字になればいいのだ。

(開始日または空文字) 〜 (終了日または空文字)

プログラムにすると以下のようになる。

from_date = "" unless from_date
to_date = "" unless to_date
print from_date + ' 〜 ' + to_date

まず1, 2行目でfrom_dateとto_dateをそれぞれ値がなければ空文字にしてしまう。その後に2つを並べて表示すれば最初の仕様が全て満たされる。わざわざ値の存在によって条件分岐する必要がなくなり、1つの表示処理で済むことになる。これは非常に簡単な例だが、もっと大きな話になっても似たようなことはいくらでも存在する。

抽象化が可能な場面では多くの場合、条件分岐が使用されている。ユーザのステータス、支払い方法、所属グループなどによってIF条件で分岐をすることはプログラムであれば当然何度も必要になる。しかしその度に以下のような疑いを持つ癖をつけよう。

・全てに共通のルールは無いか
・違うように見えていても実質同じものは無いか
・分解して同じ要素を抽出できないか

【抽象化のコツ2】 新たな概念を作ってまとめる

例えば出版社のECショップを開発していて、割引についてこんな依頼を受けたとしよう。

A. コミックは新刊が出てから1ヶ月間、そのシリーズの旧刊に10%割引をする
B. 昔から長く読まれている名著は年末年始に10%割引をする
C. 雑誌は発売後3年経過したものは全て10%割引をする

ここまで読んだあなたなら、商品情報画面でこのA~CをIF条件で分岐して10%引きするような手段は取ってはいけないと気付くだろう。だけどコツ1で書いたように大きなルールでまとめようにもコミック、名著、雑誌と条件は統合できそうにない。

そのような時は新しい概念を作ってそれでまとめ上げる。つまり「割引キャンペーン」というような概念を作ることを考えよう。DB設計で言えばそういうテーブルを1つ作ることになる。A~Cの条件を抽象的に捉えれば、ある特定のジャンル・時期に10%割引するというものだ。であれば何らかの形でそれを定義できる仕組みにすればよい。商品情報を表示するときにそのキャンペーン情報を参照し、対象商品であれば割引後の金額を表示してあげればいいのだ。

このように、顧客が言ったとおりにプログラムを作ればいいというのは大きな間違いだ。顧客の頭にはまだ「割引キャンペーン」という概念すら思い浮かんでいないことも多い。単にコミックと名著と雑誌はたまに10%引きにするんだ、という普段の業務が頭にあるだけだろう。顧客や営業はシステム開発の素人なのだ。基本的に正しい答えはもらえないものと思っておこう。それを考えるのはあなたの仕事だ。

さて、あなたがこの顧客の要望を上手く抽象化できて割引キャンペーンという仕組みをプログラムに作り込むとき、他にも抽象化すべきことがないか慎重に考えよう。今回の例でもその罠を仕込んでいる。勘のいい人はおわかりの通り、そう、割引率だ。これから先も新たなキャンペーンが作成される可能性はあり、それがいつも 10%引きであるとは限らない。キャンペーン情報として割引率も毎回指定できるようにしておくべきだ。

【抽象化のコツ3】 手を動かす前に考える

僕が新人のプログラマに教える時こういう言い方をしている。

「どんなプログラムも、書く前に頭の中で3通りの書き方を考えろ」

最初に思い浮かんだ書き方は恐らく言われた通りに実装する安直な方法だが、それでは足りない。言われたことを分解して並び替えて、整理した結果一番シンプルに解決できる方法を考えなければならないのだ。どうしてもそれがなければ最初の案に戻ればいい。

3通りという数字が正しいかどうかは状況次第ではあるが、難しいことを言っても新人の意識には残らないだろうから覚えやすいように3通り考えろと言っている。優秀な人ほどプログラムを書き始める前にしっかり考える時間を取る傾向があり、優秀でない人ほど言われた瞬間にプログラムを書き始めている。

まあしかし、新人のうちは考えろと言われても考え方がわからない人もいる。そういう人はまず手を動かしてもいいだろう。プログラムを書き終わった後にさらに2通り、別の書き方を考えたり実際に書いてみればいい。3つのプログラムを比較して、それぞれのメリットとデメリットを比較検討する中で成長していくだろう。そのうちわざわざ書かなくても頭の中で3パターンを比較検討して一番いい方法を選択できるようになる。

まとめ

対象システムのレベルが高くなるほど、抽象的なことに対して考えて開発しなければいけなくなる。例えばRuby on RailsやLaravelのようなフレームワークを作ることを想像してみよう。それを使って一般開発者がどんなシステムを作るかは全くわからないのだ。それを想像し、そこに必要になりそうな仕組みや機能を設計・開発するという、高度に抽象化された思考が結実したものである。抽象化能力が低いエンジニアは当然このような高度な仕事はできないし、そのレベルの報酬を受け取ることもない。

もちろんフレームワークを作るようなレベルではなくても、今回書いたような抽象化能力は強烈に求められる。いや、明確に求められればまだわかりやすいが、依頼者側も気付いていないため、依頼は普通の姿をしてやってくる。それを普通にこなすこともできるがそれでは初学者の壁を突破できない。ここに書いたことを普段から意識して、依頼の奥に潜む抽象化の種を見つけられるようになってほしい。

この記事が気に入ったらサポートをしてみませんか?