オブジェクト指向を分かりやすく例えて教えてくれ 2
■ このスレッドは過去ログ倉庫に格納されています
わかり易い例
class Dog extends Animal
class Cat extends Animal
オブジェクト指向とは 分かりやすく教えてくれ
https://medaka.5ch.net/test/read.cgi/prog/1521869966/ >>961
次スレ立てるのはいいけどアンチパターンを>>1に書くのは、ほんとにやめて。
オブジェクト指向を考えるときに動物を持ち出してはならない。
何故なら、プログラミングは人によるものづくりであり、動物は神によって作られた代物で人間によって作られたものでは無いからだ。
だから、オブジェクト指向に動物を持ち出すと、オブジェクト指向が余計にわからなくなる問題が起こってしまう。
オブジェクト指向の基本は、車とか自転車のように作りたいオブジェクトをパーツを組み合わせて作り上げるのが基本だ。
そして、オブジェクト指向でそれを実現するにはコンポジションを使う。
このことを知らない、動物に惑わされたプログラマは、クラスが別のクラスの機能を使う手段として継承を選択する過ちを犯す。
コンポジションとは、オブジェクトが必要とするパーツをオブジェクトに装着することであり、これは自転車のフレームにタイヤをつけて自転車が完成することととてもよく似ている。
しかし、動物で同じようなことを考えると、動物のお腹にメスを入れて胃袋を取り替えるといった奇妙な発想をすることになり、コンポジションというオブジェクト指向に必須と言える概念が、見えなくなってしまうのだ。
よって、オブジェクト指向の世界に動物を持ち出してはならない。 例えば車というobjectにはタイヤというobjectを着けることが出来る
ね、簡単でしょう >>3
そう!そしてそれがオブジェクト指向の正解なのだ! 例えば猫objectと犬objectを作ってワンワンニャーニャー鳴かせるのがオブジェクト指向
ね、簡単でしょ >>5
オブジェクト指向で、継承を利用して犬猫をワンワンニャーニャー言わせると言う話はよく聞く。
しかし、継承を使わなくとも、自転車にタイヤを装着するように、ヌイグルミにスピーカーを装着する作り方がある。
もちろん、ワンと鳴るスピーカーを取り付ければ、犬のようにワンと鳴き、ニャーと鳴るスピーカーを取り付ければ猫のようにニャーと鳴くのだ。
つまり、クラスが別のクラスの機能を使う手段は「継承」と「コンポジション」があり、コンポジションの方が頻繁に使われる。
この使い分けを謝るとオブジェクト指向はあなたに牙を剥くだろう。 うちの開発はオブジェクト指向禁止手続き型オンリーだから関係ない クラスが別のクラスを利用する方法にコンポジションがあり、継承より使える子であることは説明済みだ。
しかし、これからのスレの流れの中で、確実に混乱は繰り返されるだろう。
そこで、もう少し踏み込んでコンポジションについて解説しておこうと思う。 まず、コンポジションの方法には種類がある
1. クラス内部でnewして持たせる
2. メインでnewしてコンストラクタで渡す(コンストラクタインジェクション)
3. メインでnewしてメソッドで渡す(メソッドインジェクション)
この3つのうち、2と3を依存性の注入(Dependency injection、DI)と言う。
DIはクラスの疎結合性と柔軟性の両方が得られるため、プログラマにとても好まれる代物だ。
中でもとくに好まれるのは2のコンストラクタインジェクション
なぜなら、3のメソッドインジェクションは、オブジェクトが不完全な状態を一時的に許すことになってしまうからだ。
これは、タイヤの付いてない自転車の利用を一時的に許すことを考えてみればその危険性がわかる。
つまりまとめると、継承よりコンポジションが好まれ、またコンポジションの手段としてはコンストラクタインジェクションが特に好まれると言うわけだ。 そして、DIという言葉を出すと、必ずと言っていいほど「ユニットテスト」や「DIコンテナ」の話を出してくる人が現れる。
この人たちは、DIの本質を理解しておらず、DIをユニットテストを行う目的でしかたなく勉強してきた人たちだ。
そしてイケてると思い込んでいる疎結合性に欠けるプログラムを後から大量に書き換える必要が生まれ、DIトラウマになったのだ。
もちろん大量にコードを書き換えると、その後プログラムが安定するまでデバッグ地獄に落ちることは言うまでもない。
そのため、DIの話した時にユニットテストがどうこう言いだし、DIを拒絶する人を見かけたら
「DIの本質を理解せずに組んでしまったために、とても苦労した可哀想な人なんだな」と
温かい目で見守ってあげるようにしましょう! お前らの話は受け売りばかりで実体験が透けて見えないから面白くない まず、コンポジションの方法には種類がある
1. クラス内部でnewして持たせる
2. メインでnewしてコンストラクタで渡す(コンストラクタインジェクション)
3. メインでnewしてメソッドで渡す(メソッドインジェクション)
この3つのうち、2と3を依存性の注入(Dependency injection、DI)と言う。
DIはクラスの疎結合性と柔軟性の両方が得られるため、プログラマにとても好まれる代物だ。
中でもとくに好まれるのは2のコンストラクタインジェクション
このコンストラクタインジェクションが使われてるライブラリは何かと聞いてくるやつがいる
だがそんなライブラリは存在しない。
なぜだ?なぜなんだ。矛盾してしまうではないか >>10
> これは、タイヤの付いてない自転車の利用を一時的に許すことを考えてみればその危険性がわかる。
なんでこの例え入れたの? そもそもだ、メインでnewしてコンストラクタで渡すと言っても
渡すオブジェクトは決まっている。
なぜなら別のオブジェクトを渡したら、それはまったく
別の動きをするからだ。
DIがテスト以外で使える場合は限られていて、
処理に互換性がある場合のみだ。
例えば、ソースのアルゴリズムを変えても
ソートの結果は同じになる。処理のやり方が変わるだけで
結果が同じにならないかぎりDIで入れ替え可能になることはない
だからテストにしか使えないのだ >>15
> なぜなら別のオブジェクトを渡したら、それはまったく
> 別の動きをするからだ。
何当たり前なこと言ってんの?
自転車のタイヤを取り替えて、何も変わらなかったら意味ないじゃん。
あなたが指摘してるそれはプログラムの柔軟性でありメリットなんですけど > 自転車のタイヤを取り替えて、何も変わらなかったら意味ないじゃん。
自転車のタイヤを取り替えても、車を走らせることには変わらない
つまりソートアルゴリズムの話と一緒。
アルゴリズムは違うが結果は同じになる
タイヤを取り替えてもなにも変わっていない >>20
偏ったプログラムしか組んだことないんだろうな
レースゲームで、車のパーツを変えて1位とっても
結局ゴールまで走るゲームでしょ?って言ってるようなもん >>19
プログラミング黎明期からのプログラミングの基本なんやけれどもw >>21
レースゲームの車のパーツなんて
単なるデータの集まりだよw
グラフィックとあとは速度や曲がりやすさの
パラメータに対して補正がかかるだけ
レースゲームのパーツをいちいちオブジェクトにはしない >>24
いや、するだろww
ファミコンのレースゲームの話なんかしてないよ? >>25
じゃあ何のレースゲームの話をしてるんだよ? >>22
> プログラミング黎明期からのプログラミングの基本なんやけれどもw
ねーよw
2000年ごろからでっち上げられた言葉だ >>24
仮に車のパーツがデータの集まりだったとしよう。
例えばスタッドレスタイヤを車に装着したとする。
スタッドレスタイヤには、おそらく摩擦力とか、大きさとか、重さとかのデータが含まれているだろう。
で、そのスタッドレスタイヤはデータしか持たないのでJSONで管理してましたと。
そのJSONを読み込んだ後、CarクラスにそれをそのままDIするわけ?そんなことしないよね。
car.changeWheel()メソッドに渡せるデータ型はWheelクラスとそのサブクラスだ
そうしなければ、タイヤ部分にバックミラーのJSON装着して悲惨なことになるよ??ww >>27
あまりにもDIが認知されないから
それを問題視した人がいて、2000年頃から流行り出したとかじゃないの?
メインでnewしたオブジェクトを他のオブジェクトに渡すなんて、普通すぎる実装じゃん > そのJSONを読み込んだ後、CarクラスにそれをそのままDIするわけ?そんなことしないよね。
バカっぽい。DIって言葉を知って、なんでもDIって言ってるやつっぽい。
JSONってのも意味不明。これもJSONって言葉を最近知ったんだろうなw
単にデータを構造体でも何でもよくて普通に持たせて
メソッドの引数で渡せばいいだけ
(お前は引数渡しのことをDIって呼んでるなw)
> タイヤ部分にバックミラーのJSON
笑えば良いのか? 意味不明すぎる >>29
> メインでnewしたオブジェクトを他のオブジェクトに渡すなんて、普通すぎる実装じゃん
それはDIではない つーか、単なる引数渡しのことをDIって言ってたのか
話が通じないわけだw >>30
なんでJSON最近知ったんだろうなとか、そういう低レベルな方法で返すの?
多分あなたが最近JSONを知ったんだろうけど、JSONが嫌ならCSVでもYamlでもなんでもいいよ。
それに、構造体にして外から注入してんならそれDIじゃんww >>32
DIは2と3だって書いておいたので遡って読んでね >>35
調べれば?
https://www.weblio.jp/content/D.I.?edc=BINIT
> DIとは、プログラミングにおけるデザインパターン(設計思想)の一種で、
> オブジェクトを成立させるために必要となるコードを実行時に
> 注入(Inject)してゆくという概念のことである。
この実行時っていうのが重要で、コードで静的に書いて
単にnewの引数渡しすることはDIじゃない >>36
ゲーム作ったことないでしょ?
ゲームでオブジェクトを作る時、そのクラスはなんらかの描画モデルを継承することが多い。
例えばFlashであればMovieClipというクラスを継承して様々なオブジェクトを作る。
だからタイヤ自体はデータの集まりでも、プログラムに組み込んだ時はオブジェクトにして使うんだよバーカ >>37
ん?どゆこと?
メインでnewして作ったオブジェクトを他のクラスのコンストラクタに渡すのは「実行時」じゃないの? >>40
コンパイルした時点で渡すオブジェクトが決定しているなら
それは静的に決まっているということです。 >>36
それに全部クラスにする必要はないからね?
今回の例ではそう言ったけど、Wheelクラスのコンストラクタの引数のバリエーションでカバーできるならWheelクラスのサブクラスは要らないよ?
でも考えてみ?もし、タイヤがパンクしたときに起こる演出や効果が
全てのタイヤで異なっていたら、全部サブクラスにする可能性は十分あるよ? >>27
せやな、オブジェクト指向の普及と共に疎結合というプログラミングの基本が一部忘れ去られて
メンテナンス困難なソースコードが乱造されるようになってから慌ててでっち上げられたバズワードやでw >>41
ああ、なるほどね!言いたいことはわかった。
でもそれってどこから動的になるわけ?タイヤのコンストラクタに渡す値に乱数使ったらそれは動的なの? >>42
パンクアニメーションデータを再生すればいいだけじゃないですかねぇ
どうせアニメーションデータなんかプログラムで作らないんだし >>47
ゲーム作ったことないなら知らないと思うけど
ゲームの中で発生する煙とか火とか、火花とかそう言ったものはパーティクルと言ってプログラムで作るんだよ >>49
ごめん話脱線しすぎた。
とりあえずパーツごとにクラスを大量に作ることはあるよ。
もちろん、コンストラクタに渡すデータを変えるだけで完結できるならサブクラスを大量に作る必要はない。
サブクラスを大量に作る必要が生まれるのは、サブクラス毎に異なるコードを書く必要性が生まれたときだろうね。
また、今は必要ないけど将来的にサブクラス特有のコードを書く可能性が高い時なんかは
ほとんど空っぽだけどサブクラスを大量に作る人もいるんじゃないかな?
そういう作り方は嫌いだけどね。
また、スクリプトの利用が許されるならサブクラス毎にコード書きたい場合でも
データにスクリプト書いてサブクラスをいらない子にするテクニックもある 実際バズワードだからなw
クラスの中でしか使わないものなら
クラスの中で生成すりゃいいんだよ。
もともとクラスの中でnewしてるものは
それ以外のクラスを使うことなんか想定してないんだから
そういうものをわざわざコンストラクタで渡す?
なぜ? テスト以外考えられない もちろんアルゴリズムを入れ替え可能にしたいのであれば、
ストラテジーパターンを使えばいい。
DIじゃない、ストラテジーパターン
DIはテストのために全てのクラスに
ストラテジーパターンを適用するとかいう
愚かな行為
本来は不要な過剰な設計 今はDIしなくてもモックでオブジェクトの挙動をテスト向けに変更できるから、無駄にDIしなくなってきたね
DIはほぼユニットテストのために使われてたようなもんだってことだよ クラスの中で別のクラスをnewしてます。
それをコンストラクタのnewで渡すようにすることでー
することで?
テストではなくー
テストではなく?
別のクラスに入れ替え可能にー
いや、別のクラスに入れ替えることなんて無いから
大抵の場合、別のクラスに入れ替えることなんて無いんだよな もう少しわかりやすく説明してやるか。
そもそもDIってのはクラス内部でnewしているものを外から渡すという話だ
もともとクラス内部でnewしていれば十分だった。これが重要な所
システム上クラスを入れ替え可能にしたいなら
最初からストラテジーパターンなりを使っていたはずだ
つまりもともとクラス内部でnewしていれば十分だった場所は
もともと入れ替え可能にする理由なんて無い場所ってことだ
そこに理由をつけるならばテスト以外ありえない
テストのために不合理な設計変更を押し付けられ、
コードが不必要に複雑になる
内部で隠蔽されていたはずのクラスを、外に持ち出す必要が出てくる >>57
え、Frameworkで用意されてる規定のものを独自実装にDIで簡単に切り替えることなんてよくあるやろ >>56
ちょうどBuildで盛り上がってるとこだし、ASP.NET Coreのソース見てみろよ >>59
> え、Frameworkで用意されてる規定のものを独自実装にDIで簡単に切り替えることなんてよくあるやろ
ないなぁ。設定ファイルで別のクラスを使うように
することはあるけどそれは別にDIとは関係ない話だし
そもそもDIで簡単に切り替えられるようになっているとしたら
それはそのフレームワークがDIコンテナを使っているからで
どんなフレームワークでもDIで簡単に切り替えられるなんてことはない
つまりはDIがフレームワークに含まれているようなもので
DIで簡単に切り替えというよりも、フレームワークの機能で
簡単に切り替えと言ったほうが良いだろう
DIを使っていたからと言って、呼び出し側でハードコーディングされている
newをすげ替えることはできないんだからね DIに依存してる人ってやっぱりJavaとか使ってんの?
モダンな言語、例えばScalaならDIなんてほとんど使わないけど >>68
ほう?ScalaではDIを抽象化し去っている話、詳しく。 >>53
>>58
こういう間違った意見を堂々と言ってくれるのは助かる。
本当にクズなのは否定だけして、理由を話さない奴だからね。
言いたいことや君がどういう風に考えているかはわかった。
ストラテジーパターンというワードを出してきたのもナイスだ。
しかし、プログラミングや現実世界というものは常に流動変化するものであり、その変化に対応するため
前もって交換可能な柔軟性を持たせることがオブジェクト指向の意味なんだよ。
そしてDIはその柔軟性を持たせる大きな手段となるため、ほとんどDIすることが多い。
もちろん、交換してはならないものが存在することもある!そう言ったものは内部でnewすれば良いだろう。
しかし、大抵の場合内部でnewしたクラスというのは、標準で用意されているクラスか、フレームワークで用意されているクラスか、ライブラリのクラスなのではなかろうか?
これらのクラスはテストを行う必要がない。なぜなら、これらのクラスはプロジェクトをまたいで
世界中の人が利用している比較的完成されたものであり
テストを行ってバグがみつかることはほぼ無い。たとえ見つかっても自分たちで修正できるものではないからだ。
(もちろんバグがあり、報告したけど対応が遅いので無理やり書き換えることはあるが、そんなこと頻繁に起こらない)
何でもかんでもストラテジーするアンチパターンを適応する愚かな行為というのは、一見納得できそうな主張だ。
しかし、実際にDIするのは、自分や同僚が作ったクラスに対してだ。
そして我々が触れるプログラムは自分や同僚が作ったプロジェクトのプログラムが大半を占める。
DIによって得られる柔軟性と我々が求めている柔軟性は、アプリケーションの柔軟性であり
ライブラリやフレームワークは関係ないのだよ。
だから大体DIになるのだ。 関数型言語でもオブジェクト指向って通用するんですか
関数型やったことないのでまた新しい概念を覚えるのかと思うと漠然とした不安があり >>70
テストがちゃんと行われていればDIで入れ込まないでいいってこと?
つまりテストがちゃんと行われてないからDI使うってこと?
つまりDIが必要なのはテストするため?
やっぱりDIはテストのためか >>72
関数型とオブジェクト指向は別の概念
だけど一般に使われている言語は純粋なオブジェクト指向や純粋な関数型じゃなくてそれらのいいところをちょっとずつ含むマルチパラダイムだから今までやってきたこととまるで違うことにはならんと思う
でも新しい概念やプログラミング作法は覚えなきゃならんよ
逆に今までやってきたことが邪魔になったりもする >>73
もう疲れた>>2-11あたりを読んでください DI使っていればアプリケーションに柔軟性を
もたらされるっていうのも、眉唾ものだな
DIでできるのは所詮クラスの挿げ替えだけで
すげ替えででできることしかできない
最初から完璧な設計をすることはないんだから
すげ替える必要性が出てきた時というのは設計の変更が必要
すなわちリファクタリングが必要
結局の所ソースコードに修正ができればいいわけだ >>77
ユニットテストに合わない設計しちゃって文句言われた
俺が正しい
までは読めたよ >>73
だからASP.NET Coreのソース見てみろって ユニットテストをしっかりやったクラスは
DIで外から入れる必要はない >>76
細かいこというと「アプリケーションに柔軟性をもたせるための手段の中で、かなり使える手段がDI」と言ってほしい >>81
それはクラスの外部から渡すのではなく
クラス内部で生成しても同じことやで?
newする場所が違うだけ。前者はいちいち面倒くさい >>82
それは違うよ
抽象に対してプログラムすることで多態性が生まれる話はわかるよね?
しかしnewっていうのは具象度MAXのオブジェクトを生成するため、疎結合性を一気に崩壊させる接着剤のような代物だ。
newする場所は限られていて、それを注意しなければ疎結合は一気に破壊されるのだ。
しかし例外がって、Stringなど言語自体に定義されたクラスは言語に依存しているためどこでnewしても構わない。
フレームワークを使ったら、フレームワークで定義されているクラスもある程度自由にnew可能だ。
つまり、newするクラスのレイヤーが表面的なものであればあるほどnewの被害を受けることとなる。
だから自分や同僚が作成したクラスは大体メインクラスでnewしてコンストラクタインジェクションするんだよ >>82
その考えは、周りのプログラマに多大な迷惑をかけてる可能性が高いので
よく考えてみるのがいいと思う。 >>83
何いってんの?
Interface i = new Class ってすればいいだけじゃんw >>84
考えるの拒否してるから面倒ってワードが出てくる >>85
Java なんかは、クラス内部でnewしたとき、newしたクラスをimportしなければならなかったはず。
そのimportは依存そのものだ >>88
あーやっぱり基本的な設計レベルが低いんだ
まぁ、今時DIで騒いで実装知識が無いわけじゃないってんでphperだろ? >>89
ほら出た。叩くだけで理由を言わないそのスタンスww
プログラマの癖に生産性悪いことしちゃうプログラマの恥 >>88
それこそ言語の問題だとしか思えないよねw
newの代わりに、Interface i = GlobalClassFactory.create("クラス名") とか
やれば解決できるじゃんw
newキーワードをフックできればDIはいらないってことになる × newキーワードをフック
○ newキーワードをオーバーライド
まあ訂正するほどのことじゃないけど >>90
まーた矛盾させるわけかw
その理屈だと、StringクラスやDateクラスなど
Javaの標準のクラスまでDIしろって話になるんだよ >>92
初めからそういう風に言い返してくれるとたすかる。
ファクトリはAbstruct FactryやらFactry methodやらあるけど、どちらもファクトリを使うクラスはファクトリのスーパークラスをコンポジションするものだよ
直接newするよりかいくらかマシだけど、それやったら今度はファクトリの具象に依存してるじゃん。
だからその場合ファクトリをDIするんだよ >>95
まあ、ファクトリのサブクラスを大量に作る予定なんてありませんのでって話ならそれでもいいかもしれない
それにその場合、静的クラスを使ってることが、柔軟性を失ってることに気がついてる? 間違って自分に返信してしもうた。>>96は>>92のレスです。 >>95
> 直接newするよりかいくらかマシだけど、それやったら今度はファクトリの具象に依存してるじゃん。
依存すればいいだろう?
たかが文字列からクラスを生成するだけのコード
バグなんてまず起きないし、どうせDateとかStringとかはDIしませーんっていうんだろ?
それと同じに考えればいい
手段と目的が逆になってる
依存を無くすのが目的となってるな >>96
> まあ、ファクトリのサブクラスを大量に作る予定なんてありませんのでって話ならそれでもいいかもしれない
何のために作るんだ?w
文字列からクラスをnewするだけの唯一の
グローバルなシングルトンクラスだよ ■ このスレッドは過去ログ倉庫に格納されています