が(良くも悪くも)注目頂き、その観測で思ったことのメモです。1年後の自分用です!
もっかい言いたいこと再考のポエムです。
概要
- 関数型には意図的に触れたくなかった
- 継承や再利用性への懐疑の共通認識
- 抽象化戦略開発戦略で補う話
- タイトルは釣り
- 抽象化という言葉のふわっと感
- カプセル化が問題
関数型言語には意図的に触れたくなかった
ポリモーフィズムのくだりで、関数型のご指摘が多かったのですが、あえて直接は触れたくありませんでした。これは、オブジェクト指向 vs 関数型にしたくなかったからです。(結果、Rust/Goに被弾させました)
なぜかと言えば、オブジェクト指向を(結果として)衰退させたのは、あくまでも 開発手法の変化 や設計論の精錬が主軸だと認識しています。
不確実性に適応する上で、継承やカプセル化による状態隠匿という戦略が、良い筋に動かず、オブジェクト指向なりに変化を遂げた結果だと考えています。その変化方法が関数型プログラミングのエッセンスを取り入れるであり、単純な対立消滅関係とは異なるからです。
抽象化のくだりで混乱させてしまいましたが、インタフェースによる疎結合は近年のアプリケーションアーキテクチャに大きな影響を与えたと考えています。
継承や再利用性への懐疑の共通認識
この辺りは、改めてという感じですが。強いご指摘もなく共通認識になってるような。という感じです。今回の記事は、抽象化の乱用という視点で、もう2歩進めたかったのですが、最後の1歩がうまくいきませんでした。。
1歩目 再利用性への懐疑
2歩目 開発設計戦略で十分話
3歩目 オブジェクト指向の抽象化という輪郭の緩
開発設計戦略で十分話
2歩目。開発設計戦略で十分話です。
「オブジェクト指向プログラミングである必要が無い。」のくだりですね。
これは現場の開発者の方には、だよね。感を認識して頂いてると感じました。
具体的なコード例が弱いため、伝わりにくいところがありました。コメントでの補足で意図を掴んで頂けた感も。
いいたいことは要するに
IdProviderのような難しい輪郭を抽象化するよりも、...
大切なのはGoogleProviderやOidcProcessのような変更が少なく独立性が高い機能を導出すること。逆に、組み合わせのように変化していく箇所は、変化に追従して行けばいい。安全性は自動テスト等で十分に担保できます。
(mix)
コードとしての安全性はもちろん劣ります。ですが、抽象化をミスった際の変更リスクの増大よりはマシ感覚があります。(ただの経験)
また、自動テストするために依存を減らす必要があり、インタフェースは欲しくなりますね。
その際も、interface IdProvider
ではなく、interface GoogleProvider
で良いんじゃないって感じです。
タイトルは釣り
はいはい釣り釣り。ではなく、オブジェクト指向プログラミングが衰退するのは間違いないと思っています。インパクト的に強いタイトルを使ったことは、話題性を考えれば広域に広がりやすいことを考えるべき感はありました。
イメージラインは「オブジェクト指向が融合して、オブジェクト指向としては消えてしまう」というアレです。
なので、最後の締めは感傷的に終わらせたいな。という思いはありました。
結果、抽象化という言葉が犠牲になりました。
抽象化という言葉のふわっと感
抽象化が抽象化されてない。
3歩目 オブジェクト指向の抽象化という輪郭の緩
抽象化を否定するというより、"オブジェクト指向における抽象化"が輪郭を失ってて窮屈という話にしたかったのですが、まああまあ。。。これは書いてる途中でも良くないの分かってた。。今でも説明できてないですね。これは自分でもつかみきれてない気がします。
というわけで、ここから再考ポエムです
カプセル化が悪い
3歩目 再考 また暴論。
何を伝えたかったのか?
改めるとこんな感じ?
解決対象の抽象化と、抽象表現は段階として別であるはず。オブジェクト指向プログラミングは、抽象化と抽象表現を繋げやすい言語じゃないか?結果として、抽象化という言葉が非常に使いづらい。
本来の設計開発プロセスのアウトプットは、下記のはずなのに
解決対象 => 抽象化 => 抽象表現
(正確に言えば、モデル化が正しいかな。。。)
オブジェクト指向プログラミングは、↓を促しやすいのでは?
解決対象 => 抽象表現(とくに汎化)
具体的には
module GoogleProvider
で良いとこを、interface IdProvider
に切り出してしまいがち。
さらに、interface IdProvider
は、あんまり綺麗な輪郭にならない。
どうして、Rust/GOを対比に使ったのか
- 型システム由来の抽象化(ポリモーフィズム)を持ってる
- クラスキーワードからの脱却
- Rustの所有権のような、カプセル化の反省
- Goのオブジェクト指向のフィードバックを関数型のアイディアで解決とか、Rustのまんま関数型とか
あたり?
暴論化。なんでOOPってすぐ抽象化に走るんだ?カプセル化が悪い?
その上で改めて考える。
クラスベースの言語は、抽象表現(クラス)ありきで始まる。クラスとは、カプセル化ありきであり、そこが早期の抽象化に走らせる原因になっている?カプセル化には悪い側面があることが軽視されてるし、抽象表現を目的にしてしまうきらいを感じる。
カプセル化の問題点
悪い側面
- 状態管理を隠匿するアイディアの失敗
- 適切な場所で情報開示することが難しい
まず、カプセル化の分かりやすい問題は、別名参照問題であり、解決策としてimmutableがあるが、それは状態管理の隠匿というアイディアが崩壊気味ですよね。というそれ。
また、カプセル化は、あくまでも情報隠匿であり、良い悪いがあるはずなのに、"良い"のイメージが強く乱用されている。
"悪い"の分かりやすい例として、ValueObject(同時に盛り上がってた)の文脈で出てくる、局所化しすぎて不適切な場所からの情報開示がある。
例えば、
年齢によるサイトの利用制限がある際に、利用制限の判定メソッドを年齢オブジェクトに生やしてしまう。
たぶん、どっかにもっと良い記事あるはず。
さらに、本来開示されるべき情報さえも、不要に情報隠匿してしまうことがある。処理の流れを理解のに大きな妨げになる。Inputに対してのOutputとしてシンプルに表現した方が分かりやすいことも多い。
追記: 不要な情報隠匿について、ひっかかかる部分があったので書いてみました~
オブジェクト指向プログラミング(クラスベース)は、カプセル化を促しやすく不適切な抽象化に繋げやすいのでは?
いいたいこと
オブジェクト指向プログラミングは、抽象表現が貧弱で、とりあえずクラス(最近は、インターフェース)にしちゃう。結果、悪いカプセル化が起きやすい。
解決対象 => 抽象表現(class)
クラスベースをやめた言語は、タイプ(データ)とクラスの選択肢があり、抽象表現をするのにワンクッションが必要。
(というか、classは基本文法だけじゃ表現できないことも多い)
解決対象 => 抽象化 => 抽象表現(data/class)
とくにタイプが大きい。
無理にクラスにしなくてもデータ型と手続きのセットで出来るんじゃないか?必要になったら、部分型で抽象表現も出来るぞーと言ってる。それでもクラスいるの本当に?と。
この辺りは、Go/Rustがクラスを捨てた理由を調べると面白いのかも。
軽く調べると、Rustは0.2で実装したclassesを、0.4で削除していました。wiki:Rust
意図的に、クラス指向のオブジェクト指向プログラミングから脱却していたようです。
考えられる反論
OOP言語の問題ではなく、その人の設計能力の差だけかも。
=> クラスベース以外の言語だと、気づきを与えるので違うかも?
クラスが無いからってオブジェクト指向じゃないとは言えない
=> ですよねー。あくまでもクラスベースを主体とするべきかもしれません。
クラスを持たないからって、カプセル化やめたわけじゃない。
=> ですよねー。単純に継承戦略やめたかっただけかもしれない。クラス以外の選択肢を明示してるのがポイントなのかも
追記
勝手に拾わせて頂きました。
"考えられる反論"のところによいヒントがありそう。OOPというより認知や構造主義のほうに目を向けないといけないのでは。https://t.co/i1p6Wdh6z9
— かとじゅん (@j5ik2o) August 2, 2022
なるほど。認知の視点として考えることも出来そうですね。OOPから離れてない話になってしまいますが、OOPのメンタルモデルが現実世界に向かってるのに対して、FPのメンタルモデルは"数学的"に向かってると考えると、FPでは、解決対象の抽象化(モデル化)が必須になります。そのワンクッションが良くも悪く必要になり、それを良く捉えると抽象表現を焦らないと言えてしまう気もします。
構造主義はキーワード的なイメージ湧くのですが、言語化が難しい。。