見出し画像

オブジェクト指向の本当の姿が見えてきた

UdemyのObject-Oriented Programming (OOP) 学習コースでC# でオブジェクト指向の学習中。2周目をやっている。1回見ただけでは頭に入らない。あまりにも概念が多い。だが2周目で見えた、というかようやく理解できたことも多かった。それだけOOPは深い。複雑な構造だ。

ではなぜそこまで構造を複雑にしてまで、シンプルな考えで上から順に書いていけばいい「プロシージャルな考え方」を捨ててまで、OOPはそういう複雑な手法になったのはなぜか?

(約 2,800文字の記事です。)



単一責任の原則を守るため

SRP、Single Responsibility Principle、あ、これ日本語で何て言うんだ?Google検索。単一責任の原則、というらしい。ま、英語でそう書いてあるんだが……。要するに「1つの機能しか実行しませんよ」というシンプルな原則。これを守るため、各クラスのメソッドは「自分の担当じゃない処理内容は処理しない(させない)」ようにするために(というかプログラマがそう仕向けるために)、どの処理内容はどのクラスのメソッドとして実装すべきか、あるいは実装済みの処理内容を担当するメソッドはどのクラスに書いたか、をコーディングする必要がある。

なのでプログラマはコーディング中は、まるで通信機器のルーターのように「どのクラスにメソッドを追加すべきか、あるいはどの既存のメソッドを呼び出すか」を考えてコーディングすることになる。

ではなぜこの原則を守る必要があるのか。


スパゲッティー状態を回避するため

単一責任の原則を守りながら機能拡張を行なうと、既存の仕組みを書き換えないので、追加した部分の動作の正常性を確認できれば、それでプログラム全体の正常性を確認できるようにするためだ。具体的にはこれを実施するっための概念として、

  • OCP, Open-closed Principle、えっと日本語は、開放/閉鎖原則、なんだってさw ますます分からん。英語でそう書いて(略
    機能拡張はオープンにどうぞなのだが、完成済みメソッドの中に手を入れるのはクローズなのでNGよ。

  • オブジェクトの置換性(これはかっこつけて英語にしなくても分かる。念のためObject substitution)継承クラス同士で作ったオブジェクトは入れ替え可能。これにより継承&継承クラスのオブジェクトの組み合わせが無数になったとしても、それをメイン関数で利用していても置換可能なので各クラスの種類を考慮しなくてよくなる。新たに増設したクラスのオブジェクトでも、そのクラスの上のクラスを使ったコードでメイン関数が書かれていれば、機能拡張時にメイン関数のコードにはほとんど手を入れなくて済む

分かりやすく言っちゃえば、ガンダムのパーツを交換したり、ミニ四駆のパーツを交換してパワーアップ?ドレスアップができるのは、各パーツに互換性を持たせたからだ。OOPの機能拡張もそれに近い。なので「各部の寸法の統一化のルールを守る」ことでそれが実現されているわけだ。プログラミングでも然りで、コードデザイン上の、OOP流のルールを守ることで、機能の増加を容易にするわけだ。(減らすことは滅多にないと思うが、もちろん機能を削除することも可能。安全な取り外し。USB接続?w)

USBも統一規格だから、色んな機器をPCやらPS4/5やらに接続できるわけね。ある意味OOP。


OOPでは注目点が異なる

なので上から思いついたままコーディングするプロシージャルなプログラミングとは、まったくアプローチ方法が異なる。おそらくOOPはコード設計計画=コードデザイン計画が最も重要。そこが固まってしまえば実はコーディング量はそんなに多くない。というか、少なくなるのが最も正しい実装だ。Expression-based syntaxで、2行で済むメソッドが理想。

        public Date GetDateByAge(int age) =>
            _birthdate.AddYears(age);

SRPで機能を単一化すれば、そんなに複雑はことはできない(しない、させない)。複数処理の組み合わせは、各メソッドの連続呼び出し(チェーンなんちゃら、忘れたw)で実現できる。

        public Date GetBeginning(Child schoolchild) =>
            schoolchild
            .GetDateByAge(_minAge)
            .GetFirstOccurrence(_cutoff)
            .GetFirstOccurrence(_schoolStart);

見やすく改行しているけど、これも内容的には2行コード。


OOPはデータのたらい回しが正解

上のコードを見れば分かるが、色んなメソッドにデータを投げてたらい回しで最終結論を得ている。これがOOPの正しい使い方だったのだ。最初の処理に必要なクラスに、必ずしも全部の処理担当メソッドがあるわけじゃない(当たり前w)。なのでそれで手に負えないデータは、対応可能なメソッドにデータを回す。これの連鎖で処理を終える。この組み合わせは、欲しい処理によって異なる。この組み合わせを考えるのがプログラマの仕事。

まったくプロシージャルな見た目じゃないし、処理方法も然り。だから最初はまったく理解できないわけだ😖

なので今現在で足りない処理機能が発覚したら、どのクラスに、何という名前のメソッドを、どんな引数で実装するか?を考えるのもプログラマの仕事。

そう、コードの構造を考えること、つまりコードデザインが最も重要な仕事になる。この考えが理解できるまでにかなり時間がかかった。上から下に順番に要求通りにカタカタとコーディングするのは、姿はカッコいいしタイピングで悦に入ることはできるのだが、実はコーディング以外でやるべきことがたくさんあるわけだ。

だから次はUMLを学ぶ予定。


本当に大切なことは全体設計

これは予感だが、OOPの開発工程を正しく身につければ、実はコーディング自体はそんなに量は増えない。正しいOOPでコードデザインすれば、重複コードは発生しない。無駄なコードは増えない(増やさない)。なのでコード自体がシンプルになるはずなのだ。

だから時間を掛けるべき内容はコードデザインやデザイン手法だと直感した。ここが下手なら泥沼になる。書いたコードが全部パー。全部やり直し。

つまり、なんちゃってOOPは機能拡張と共にスパゲッティーになる。あるときまでは完璧でも、ある時点でなんちゃってOOP実装したら、そこからデスマーチが始まる。OOPは完璧にOOPであるときだけ、すんなり機能開発ができる。Dead or aliveなのである。

Aliveの側に立つために、今、OOPをちゃんと学習中。


今回の創作活動は約1時間(累積 約3,905時間)
(1,150回目のnote更新)


読んでくれてありがとう。気長にマイペースに書いてます。この出会いに感謝😊