グマテック流OOPとは何か?
グマテック流OOPの定義
プログラミング成果物の「質と速さ」を「グマテックが」担保するための開発手法です。
グマテックのOOPはしょぼい
グマテックでは、原則として「オブジェクト指向(OOP)」を採用しています。
とはいえ、ここで言うOOPは、教科書的な正統派OOPでもなく、 ちゃんとした人が定義した権威的OOPでもありません。 あくまでグマテックによる「車輪の再発明OOP」のことです。 そういうとしょぼく聞こえるかもしれないし、実際そうなのですが、 「しょぼい代わりに現実的でそれなりに有効」というなんとも夢のないものになってます。
なんでそんな風にわざわざ縮小再生産しているかというと、 実はOOPの現状というのは結構ふわっとしていて、年代・思想・環境によって定義が180度違ってしまうこともあり、 「同じ言葉を使っているのに話が噛み合わない」というのが普通に起こるからです。 実際、それで事故っているのを見ることも珍しくなく、 当然それはすごく困るので、「お前らちゃんと明確化して認識合わせしとけよ」 という話なのです。
そこで本稿では、 「グマテックがどうOOPを定義し、どう使っているのか?」
を正直に公開します。 カッコつけてGOOP(Gumatech-OOP)と命名します。
そして、何度でもエクスキューズ(言い訳をカッコよくいうやつ)しますが 「GOOPはしょぼい」です
しょぼくてすみません
しょぼい理由は明確で、 最大集合のOOPの部分集合でしかないからです。 要は、歴史的に大量に議論されてきたOOPトピックのうち、 いいと思ったものだけピックして、ちょっとだけ独自の解釈を加えたものだからです。 しょぼくてもいいという方だけ、この後をお読みくださいませ。
また、グマテックの主戦場は「小規模~中規模のWebシステム」なので、 その前提をご了承くださいませ。
使えるしょぼさ
そんなグマテックがGOOPを採用する理由は簡単で 「GOOPを使うと、質も速さも良くなるから」 です。 まぁ、道具というか手法なので当たり前ですね。 効果がなければ誰も使わないですし… と言いたいのですが、 実は「効果がないのにOOPを使う人」は、現実にまだこの世にそこそこいます。 筆者としては「道具はしょぼいくらいが使いやすい」と 思っているのですが、そうは思えない人々も確かに存在して、 若干話を混乱させているので、その話も書こうと思います。 その前提としてそもそもOOPって何だよ?も記してから、 後で対比としてGOOPの定義を明記する方がわかりやすいと思うので、 そうしますね。
オブジェクト指向はオブジェクトを使うやつ
Wikipediaには 「オブジェクトという概念に基づいたプログラミングパラダイム」 と書いてあって、まぁその通りです。 詳しく知りたい方は ググるかAIに聞くといいですが、 とにかく 「数ある(と見せかけてそんなに多くない)プログラミングのやり方の一種」 で、世間的に一番普通のやつ。
このくらいで捉えておけば十分です。
オブジェクトの意味は気にしなくていい
で、オブジェクトって何だよ? っていうと、ここも色々な流派がいると思うので、 あくまで筆者としての解釈ですが。
プロパティとメソッドを持っているやつ となります。
例として人間を出すと、
- 性別・年齢・握力 → プロパティ
- 歩く・歌う・確定申告する → メソッド
といった感じです。 「ステート」「関数」「ロール」「エンティティ」など言い換えはいくらでもありますが、 重要なのは名前やオブジェクトの定義云々ではなく、 プロパティとメソッドのやつの単位に分割して考えることができるよというところです。 要は、プログラミングに限らず昔から言われていることで、 「全体だと難しくても、簡単な単位にまで分解すればそりゃ簡単だよ」 ということですね。
そして念押しです。 大事なのは「オブジェクトってものに変に意味を見出さないこと」です。 オブジェクトは、特に現実の何かを象徴したものでもないので、単にふ~んとそのまま捉えるのが大事です。 プロパティとメソッド。そんだけです。
オブジェクトはやりとりする
そのオブジェクトをいっぱい組み合わせるのがオブジェクト指向です。 筆者としては オブジェクト同士の“やり取り”で、システム全体の目的を達成する設計手法 ということになります。 やりとりのマナーみたいな話もありますが、 それは後の方で説明します。 とにかく、やりとりはやりとりです。
OOPにもモダニズムがある
ここまでが、グマテックなりの精一杯のOOP説明です。 おそらく、そんなに変な説明じゃないはずで AIとかに読ませたら「モダンOOP的なものを薄めてしょぼくした定義ですが、そこそこ合ってます」 くらいの評価は得られると確信してます! ※しょぼいというエクスキューズはしているのでいいですよね!?
で、ここで急に「モダン」って言葉が出てきましたね。 こいつが厄介で、執筆時現在(2025年)では、 ゴリゴリやっている人は「モダンOOP」を「自然に使ってる」はずなのですが、 それに対し、それなりの数の人が「レガシーOOP(と使っている本人は言わないでしょうが)」も使ってるので、最初に書いた変な齟齬が生まれているよ。 ってことなんですね。
虫歯があれば歯医者に行く
このモダンVSレガシーをまともに説明すると大変ですし、 筆者にそこまでの知能もないので、雑な例え話で雰囲気を説明しますね。
下記のような問題にどう答えるべきでしょうか?
Q : あなたは、朝起きると歯が痛いことに気づきました。 次の選択肢のうち、どちらを選びますか? 直感で選んでください。 (心理テストじゃないです)
- 歯医者は怖いしもっと痛いから、気のせいかもしれないし放置する
- 歯医者は怖いしもっと痛いけど、後から楽なので今劇痛に耐えて治療する
あなたはどちらを選びましたか?
答えは…
- 痛くない治療法でさっさと虫歯を治す
です!
3番なんてなかったじゃん!という風に思うかもしれないが、 まさにそんな感じなのが「それぞれのOOPの問題解決の態度」 だったりします。
歯医者の例えは
1 特に何もせず放置 = レガシーOOPが嫌いな態度 2 未来のために今を犠牲にする = レガシーOOP 3 今の問題は今解決。未来に問題は未来に解決 = モダンOOP
と言い換えると分かりやすいと思います。
なんでレガシーOOPは今を犠牲にしたのか?
すごく極端に言うと、 当時のOOP界隈というか開発の状況が「すごくカオス」だったからです。 みんなが好き勝手に無秩序なコーディングを繰り返した結果「虫歯だらけシステム」が大量生産されていたのです。 要は、「もう全部の歯が虫歯になってるから、たとえ激痛でも今全部抜くしかない」 な状況ばっかりなので、その対処法としてのレガシーOOPが発達・体系化していった流れがあったのです。 確かに、その状況であれば「一番合理的な選択肢は、今を犠牲にすること」であったのも無理ありません。 つまり、「今かなり手間がかかるけどちゃんと作っておけば、未来の作業が楽になって、 全体としては楽になるので、今を犠牲にするんだ派」というわけですね。
今を犠牲に得られたものがしょぼかった
しかし、実際に起こったことは「今を犠牲にしたのに、未来も楽にならなかった」でした。 考えれば当たり前なのですが、なかなか未来の変更や修正を予測できる人はいません。 なので、「今を犠牲にして作ったコードが結局使わない」 「使わないどころか、新たな問題の原因にもなっちゃう」が多発しだしたのです。 これがレガシーOOPってダメじゃん!とモダン派が思った理由です。
※実際は大規模むけの方法論が、徐々に中~小規模システムにも降りてきた。 という感じの中での、あくまでグマテックの主戦場での話です。 実は、大きなシステム&先読み達人エンジニアの場合は、 レガシーOOPで大成果を上げることも多かったので、本当はレガシーOOPが悪いわけじゃないのですが、 というか実際はちゃんとした手法なので、そこはフォローしておきますね。 逆に、大規模システムのOOP失敗の火消しの現場も見たことあるので、 結局は規模に関わらず「手法を使いこなす人と、手法に使われる人がいる」という、 悲しい事実が全てなのかもしれません。
健康な歯も抜く人
先ほどの選択肢で 痛くない治療法でさっさと虫歯を治す方法があるのに、 とにかく今激痛を味わいたい人がいるのは謎に思えるかもしれませんが、 それだけ「今を犠牲にすると、未来が良くなるんだ」と言う信仰が強かったと言うことなのかもしれません。 まぁ、それ自体は信仰なので構わないのですが、現代で困ってしまうのは 「別に今何も問題が起きてないのに、何故か今苦しみたがる人」がレガシーOOP使いとして存在していることです。 例えると「将来虫歯になる可能性を消すために、わざわざ健康な歯を抜く人」ですね。 クレイジーですよね。でも本当にいるんですよね。 当然その人たちは、先ほどの「レガシーOOPの敗北」に、そもそも気づいていないのです。
モダンOOPは収斂進化
それに対してモダンOOPの骨子は「健康な歯は抜かない」ことです。 要は「未来のために今を犠牲にしない」が重要な概念です。 この意味で、完全にアンチテーゼになっていると思います。 そして、このモダンOOPの特徴は「ゴリゴリでリアルに開発をやってきた人たちが 誰に言われるでもなく自然に同じ結論に行き着いた」ところです。 かくいう筆者もその1人で、記憶にある限り誰かに教わったことはないはずですが、 「(レガシー)OOPではこう言うけど、ダメじゃん」と言う部分を勝手に不採用にしていって行き着いた感じでした。 レガシーOOPの中の不要なものを削っていってできたので、 大げさに言うと収斂進化と言ってもいいのかもしれません。 なんか、真理っぽいですよね。 まぁ、「自然に使ってる」と最初に書いたのはそういう事情なのです。
一旦立ち位置をまとめます
ここまでがグマテックOOPの立ち位置の説明でした。 まとめると
-
レガシーOOP → 今を犠牲に未来を楽にする手法
-
モダンOOP → 今に集中する手法
-
グマテックOOP → モダンOOPの流れ+グマテック流
と言う具合です!!
ここからは具体的内容
この後から、GOOPのリアル開発寄りの具体的内容を書いていきます。 基本的にはエンジニアの方向けにはなりますが、 コードとかは載せないので、興味のある方でしたらそこそこ得るものが ある可能性がないとは言えない可能性もあるので、 お好みでご覧くださいませ!
GOOPの究極の目的
プログラミング成果物の「質と速さ」を「グマテックが」担保すること
この「グマテックが」というのが大事です。 「誰でもどのような状況でも使える手法は嘘」ですからね。 なので、申し訳ないですがGOOPは「グマテック的な人専用の」手法なのです!
以降では、GOOPの条件を規定してから、 その後で条件に対応する中身を晒すという流れにしています。 まずは「誰のためのGOOP?」からです。
「誰のもの?=グマテック的な」人の制約条件
グマテックな人。もとい筆者のことなのですが、 ざっと下記のような感じの条件設定です。
どのような開発か?の条件(外部制約)
下記の条件を全部満たしていること
- クライアントワークである
- 開発は原則として1人。もしくはごく少数で完結する
- 対象は 小~中規模 or 大規模のモック/PoC が中心
- 長期運用フェーズは主戦場ではない
※長期運用について: 初期開発と初期運用(~1年程度)が主戦場です。 それ以降の長期運用も対応可能ですが、 その場合はクライアント側にコードを引き継ぐか、 別途保守契約での対応となります。
どんな人間か?の条件(内部制約)
下記の条件を全部満たしていること
1 多彩性
ビジュアル~アニメーション~実装までシームレスに関わることを好む。 開発技能ごとのフェーズに無頓着で、一貫性を重視する。 つまり、「全部やりたい」
2 構造信仰
何事も、構造を明確にし見通せていないと思考が進まない認知特性がある。 「とりあえず何も考えずに進めよう」ができない性格。
3 合理主義
流行・慣習・ベストプラクティスでなく、状況に対する合理性のみで判断することを好む。 さらに言えば、論理と感性は同源と感じているタイプなので、 「そういうもんだから」の一旦納得が苦手。
4 凡人
通常の認知能力を持つ、極々普通の人間の想定。 天才的な記憶力や、構造把握力や卓越した数学力も当然ない。
前述の条件からGOOPの必要条件を導出すると…
前述の外部制約と内部制約を両方加味すると、 GOOPはざっくり下記の条件になります。
1 構造のないソースではないこと
- 開発者にとっての認知負荷の高さが予想される
- 認知負荷が高いと「速さ」に悪影響を与える
- また、構造がないとバグの温床になり「質」も低下する
例えば、処理の流れを羅列しているだけなのに、なぜかバグらない! みたいな天才が書きそうなコードはそもそも無理という話です。
2 筆者が把握できない種類の構造ではないこと
- 1人で全体像を保持しながら進める必要がある
- 構造信仰により、理解できない時点でフリーズ
例えば、構造があってもその組み立てとか粒度が筆者にとって意味不明だったら、 認知負荷がかえって高いなぁ。という話です。
3 構造設計に合理性があること
- クライアントは合理性を期待している
- 合理的でないものはそもそも作れない
例えば、仮にクライアントがある特定の構造をオファーしたとしても、 それに合理性がなければ否定するべき。という話です。
4 質と速さを向上させる構造であること
- 構造は構造それ自体を目的化してはならない
- 質と速さが向上できない手法は、すべて不採用にするべき
「質」とは具体的には:
- バグの少なさ:テストしやすく、問題が起きにくい設計
- 保守性:後から修正・機能追加がしやすい
- 可読性:コードを見れば何をしているかすぐわかる
例えば、すごく美しい構造だとしてもシステムとしてバグまくってたら、 誰得?という話です。
そんな前提でOOPを再定義すると
これらのグマテックバージョンの条件を元に、OOPを再定義してみましょう。
1 オブジェクトとは「凡人が一度に把握できる範疇のコードの塊」
凡人(筆者)にはシステム全体(大きい構造)を一度に把握することは不可能 ↓ それでも構造は欲しいので、「理解できる範疇に分割」する以外の方法はないなぁ ↓ じゃあ、その分割したやつをオブジェクトと呼ぶことにしよう ↓ オブジェクトの定義は「凡人が一度に把握できる範疇のコードの塊」となる ↓ 全体を分割したものがオブジェクトなので、全体はオブジェクトの集合と言える ↓ オブジェクトで構成されているから、これはオブジェクト指向。間違いない。
2 オブジェクトには「覚えてなくても大丈夫」な仕組みが必要
オブジェクトの把握はできたとしても、それの機能を覚えておけるかは別問題 ↓ でも、覚えられないと、毎回思い出したり読み解いたりするから、明らかに質と速さに悪影響あるな ↓ しかも筆者は記憶力が悪い方だし ↓ じゃあ、オブジェクトの要件に「単純な数種類の決まり事しかなく、見れば何かすぐに分かる」を加味しよう。 そうすれば覚える必要もないし。 ↓ まぁ、とりあえずベースの決まり事は2つくらいなら忘れなさそうだから、 「プロパティ(持ち物)」と「メソッド(行動)」が基本ってことで ↓ そのプロパティとメソッドを使って「何をやるかは一つだけ」にしとこう。 複数あると忘れちゃうし、見てもわからないかもだから
3 オブジェクト同士は一蓮托生にしない
凡人(筆者)にはオブジェクトを最初からいい感じに組み合わせることは不可能 ↓ どうせちゃんと動くには一発ではできないので、トライ&エラーは前提だなぁ ↓ となると、しょっちゅう全部が連鎖して壊れるとかは困るなぁ ↓ オブジェクトの位置を組み替えたり、内容がどんどん入れ替わっても「そんなに他に影響しない」必要があるぞ ↓ ということはオブジェクト同士は「一蓮托生にしちゃダメ」だな
123をまとめると
オブジェクトは把握しやすさ重視 オブジェクトの中身は忘れてもいい オブジェクトを一つくらい変えても、全体には影響が少ない
→どうやら、オブジェクトはしょぼければしょぼいほど良さそう!
結論:しょぼさが正義なのが「GOOP」
GOOPはしょぼい。だから良い
どうですか?何かすごくないですか?
筆者としては、この「論理的に導かれた結論」を信じて、 日々お仕事をさせてもらっています。 幸いなことに、今までそんなに問題を起こしたことがないのは、 「しょぼさを誇りに思ってきたから」というと変かもしれませんが、 本当にこれが言いたいことなのでございました!
ここからはエンジニア向けの補足です
非エンジニアのクライアントの方は、ここまでで十分ご理解いただけたかと思います。 この後は、より技術的な詳細(OOPの各種概念との対応関係など)を説明していきます。 興味のある方のみ、お読みください。
しょぼさのHOW
ここまでがGOOPの骨子で、実際の思想のほぼ全てです。 大体雰囲気はお分かりになったかと思います。 この後は「じゃあ実際どんな感じなの?」な、いわゆる「How」をレガシーだったりモダンだったりなOOPの用語や、そのほか色々な用語とマッピングしながら説明していきたいと思います。 しかし、最初の方に書いた通りGOOPは「部分集合」なので、導出の経緯はともかくとしても、 その内容に目新しいことはほぼありません。ただ、その一つ一つの取捨選択に、明確な論理(というか制約のために仕方なく)があるというところだけがやや優れたところです。 後は、筆者が「全部やるラー」なためのいくつか特殊な方法論が追加されているくらいです。 そこらへんを念頭に入れ、あまり過剰な期待をせずにご覧いただけると幸いです。
GOOPのツボは「しょぼさ折り込み済み」なこと
GOOPには「認知特性がしょぼいくせに全体を把握したがるし、 天才でもないくせに構造にこだわるし、合理主義者気取りのヤバいやつ」という使用者(筆者)の前提があります。 これがしょぼさの原因です。 このしょぼさを言い換えると「認知特性を甘くみずに、最優先事項として捉えること」となります。 なので、GOOPのレガシーOOPやモダンOOPからのピックアップの取捨選択基準は、 「しょぼい人でも使えるか?」としています。 ここがとにかくツボと思うので、理解がしやすいかと思います。
使いこなせない”すごい”道具よりも、100%使いこなせる”しょぼい”道具 の方がアウトプットの質も速さも高い。 という確固たる経験則が、このツボの根拠です。 つまり、GOOPはしょぼさ至上主義に立脚しています。
最重要は「疎結合」のみ
なので、いろんな概念を詰め込んでも「覚えられない」ので、 GOOPの重要事項は一個だけです。 それが「疎結合」です。 疎結合が、GOOPの全ての唯一の上位概念になります。 これは、古典OOPでもお馴染みの「オブジェクト同士のやり取りは最小」なやつですが、 少し違うのは「疎結合以外は全部オプション扱い」なのがGOOPです。
- オブジェクトはしょぼい=実装者の認知特性的にほどよい粒度にする
- オブジェクトを変えても影響が少ない
- オブジェクトの中身は忘れてもいい
- オブジェクト同士はやり取りする
↑この筆者の制約条件を言語化すると「疎結合」となる。 というだけの話です。
疎結合の言い換えは無視します
ほぼ同じ内容の言い換えがこの世にはいっぱいありますが、 無視して疎結合で統一します。
これらは疎結合の一言でまとめちゃうの立場です
- カプセル化
- ブラックボックス化
- メッセージパッシング
- 責務の分担
- 単一責任原則
- インターフェース
あまり気にしないやつ
下記の概念はOOP界隈でよく出てきますが、基本的な態度は「気にしない」です。 認知負荷ハッピーで作っていたら、自然に出てくるよね。的なものはそのままOK。 出てこないものは、無理に使わない。 それだけです。
- コンポジション / DI
- 抽象化
- ポリモーフィズム
- 再利用性
- オブジェクトの状態云々
- 集約 / 関連云々
- ライフサイクル云々
疎結合と相性悪いから不採用なやつはこちら
下記の概念は、明確に意識して排除するべき「認知特性の敵」です。
密結合
密結合は認知をしにくいので、GOOP的には禁止です。 具体的には「オブジェクト同士が天才にしか分からない結合をしていたら間違い」 です。 しょぼい方が偉いです。
※ただ、「なぜか超高効率で動く、天才がわざと密結合で書いたソース」 も存在するし、それが必要な局面もあります。 なので、密結合が悪いのではなくて、あくまでGOOPでは。って話です。
クラスの継承
そもそもOOPはクラスベース前提でもないですが、 継承は密結合そのものなので使いません。 得られるものも、わずかかな?と思います。
DRY(再利用性)至上主義
DRYは大事かもしれないですが、 「1箇所の修正で済んでラッキー! 」の便益 よりも 「一蓮托生の悲劇 」のリスク が上回るケースの方が多いので、GOOP的には慎重派です。
認知特性的な理由で不採用なやつはこちら
抽象化
抽象化すればするほど認知負荷が上がるので、 基本的には「ほぼしない。しても1階層くらい」です。 しかし、抽象化したほうが逆に認知負荷が低下する局面なら するかもしれません。
SOLID原則
これは大規模系の話なので、基本無視です。 多分Sだけ部分採用、という感じです。
GOF
これは、言語とかフレームワークに吸収されているコンセプトが多いので その通りに使用する。という立場です。 と言いつつ、実際は「多すぎて覚えられない」の方が本当の理由です。
GOOPのベストプラクティス
本稿の最後に、実際のGOOP手順をざっくりと記します。 クライアントの方は発注の際の参考にしていただけると幸いです。
1. システム全体の目的やあり方をしっかり定義する
ここはグマテックのデザイナーとしての思考法(別途執筆予定)で、 しっかりと言語化します。 おそらく、この工程が重要度の大半を占める気がします。 多分、70%くらいはここの質かもしれません。
2. 1の定義をもとに「出てきそうなオブジェクトに全体を分割」する
「プロパティとメソッド」を設定しながら、 「認知しやすい粒度」のオブジェクトを羅列します。
3. パズルを組み立てる
オブジェクトを使って、疎結合を意識しながら、 全体の目的に沿うようにパズルを組み立てます。 一番楽しい工程かもしれません。
4. 妥協オブジェクトを3つくらい設定する
全体の8割くらいの設計が終わると「なんか意にそぐわない箇所」は 必ず出てきます。シンプルじゃないオブジェクトとか、局所的な密結合とか。 しっくりこない繋がりとか。 そういう場合は「無理せず諦めます」
※「3つ」という数字は経験則です。小~中規模のシステムでは、 全体で2~4個くらいの「理想的ではないが必要な妥協」が出てくることが多いです。 重要なのは、妥協を意識的にコントロールすることです。
速度も大事なので、100点の設計に時間を取られないようにします。 なので、なんか監督するクラスとか、処理の流れだけのクラスとか、 そういうのは絶対出てくるので、全体で3つくらいは意図的に妥協します。 欠点は、制御可能な範疇に収まってればいいのです。
5. 言語化して全体をチェックする
全てのオブジェクトが「何のための存在しているか?」を パッと答えられるかどうか?でチェックします。 例えば「画面の描画を監督するクラス」とかならOKですが、 「何か来るかもしれないし、こないかもしれない入力を、その情報の内容によって、 何かしらのこれもまた外部から設定されたアルゴリズムに従って分岐させる時もあれば、しない時もあるクラス」 とかがあれば2からやり直しです。
6. ちまちま実装する
実装します。
以上です!