読者です 読者をやめる 読者になる 読者になる

複数個を前提とした設計はオブジェクト指向で綺麗な設計か?

id:kmaebashiさんへのレスになります。
コメントありがとうございます。

みねこあさんのところに書いたことと重複しますけど。

>適切に設計されていれば、記述が増えるだけでCでも可能なわけです。

これは当然で、だから私はオセロの例ではまずCで記述しています。

http://kmaebashi.com/programmer/object/othello.html

このへん↓で作っている言語も、Cで実装されていますが、コンパイラインタプリタは複数生成可能な実装としています(コンパイル中だけは静的データを使う)。

http://kmaebashi.com/programmer/devlang/index.html

了解です。
自分が気になったのは件の頁が
「Cだと、データを「ひとつだけ」抱えることによるデメリットがあり困りますが、複数個インスタンスをつくれるオブジェクト指向ではそんなことはありませんよ」
というようなニュアンスに読み取れたからです。
Cではできない、C++Javaならできる、というのは事実無根であり、あれ、っと思ったからに他なりません。

オセロゲームでもcell配列がstaticになっていますが、
これらは単一だろうと複数だろうとどちらにせよ構造体にすべきでしょう。
cellとは何のセルなのか? ということを扱う際に単なる盤面の表示状態だけでなく、CPUの思考なども含めると、
cellがstatic配列だけで片付くと考えるのは早計だからです。
(次にプレイヤーが置くことができるセルの表示などなど、概してcellでは片付かないでしょう。それならば、それらは纏められる(構造化)されるべきものです。

また、ネットワークオセロになるのであれば複数の盤面を扱う際に、そのデータの所有者は誰なのか、何を送信すべきか、何を受信すべきか、
そもそもデータはサーバにあるのか、クライアントにあるのか、
などといったことを考える必要があり、これらは実装上の最低限です。

その中から「複数の盤面を扱うこと」を指して、複数のインスタンスを扱うことに繋げ、

つまり、これがオブジェクト指向です。

というのはあまり正確ではないと思います。

そして、このようにオブジェクトを必要な数だけ生成し、 それに付属した関数を呼び出しながら動作していくというプログラミングスタイルが、 「オブジェクト指向」であるわけです。

というのも賛成しかねます。
これではまるで「Cでネットワークオセロを組み、最低限の事を満たしたら、それはオブジェクト指向になる」ように読み取れてしまうからです。

>ので、C経験者であろうと何であろうとCanvasが「ひとつしかない」という
>想定をするとしたら、それは単に経験が足りないからではないでしょうか、

部品化とか再利用とかを言うのなら、「フツー複数要るでしょ。わからんやつは経験不足」ではなくて、「常に複数になることを意識する」必要もあると思いますけどね。引数で参照を持って回る手間を考えて割に合わない可能性は十分にありますが、なら使う側でCanvasをstaticに持てば済むこと、Canvas自体をstatic前提で作るのはあまり賢くない気はします。

自分は少し違う考えです。
複数いるのか、単数なのか、それとも固定の数だけなのか、は状況に依存します。
故に、そのデータの扱いがどうなるべきなのかを見極めるのがまず必要です。「常に複数になることを意識する」では何でも複数有るとみなしてしまいそうではないでしょうか?
アプリケーションの中には「単一であって欲しいもの」も当然存在するわけで、それらを複数になることを意識する必要がないのにも関わらず、複数対応にしてしまうと「あっちゃー」ってなることがあります。

例えば、またゲームで申し訳ないですが、スタンドアロンのゲームで行われるオセロの盤面に似ている「キャラクタが歩くマップ」という存在は多くの場合、単一の存在です。(ネットワークRPGでサーバ側だと違いますが、それはさておき)
複数のマップを同時に開いている必要性はほぼ無いです。(先読みすることはありますが、先読みしているマップと現在動作しているマップとは異なります)
これらを「複数になることを意識する」ことは多くの場合、無駄な(やらなくても良い)意識です。
即ち必要になってから考えれば良いことです。(ネットワーク化、対戦、特殊なシチュエーションなどなど)

プレイヤなんかもそうで、「一人のヒーローを操作するゲーム」と「複数のヒーローを操作するゲーム」がありえますが、一人のヒーローを操作するゲームで、
「複数になったら……」ということを考えて「部品化」「再利用」と言いつつ、複数操作に対応してしまうのは無駄な作業です。
これらは「オブジェクト指向にかこつけた無駄な作業」であるとしか見えません。

まあこのへんにはトレードオフがあるでしょうが、たいていのクラスは、「複数作成されること」を前提とした設計になっています(クラスとインスタンスの関係は、本質的にそういうものですよね?)。だったらまずそこから説明しなければ、そりゃわかってもらえないでしょ、ということです。

なので、まさに自分はトレードオフが大事であり、「複数作成されること」を前提とするより、「そのアプリケーションでどう利用されるか」を前提とすべき、
と考えます。複数つくらないといけないから、クラスにしてインスタンスとして生成しないといけない、というのはあまり直感的には感じられません。
例えばモデルやモーション、BGM、エフェクトなどなどリソース管理クラスなどをつくることもありますが、これらは複数のインスタンスを持つ必要はあまりありません。
下手に複数インスタンスを持つようにすると持ち回りが無駄に大変になります。
キャラクタやアイテムのパラメータ管理クラス(攻撃力や防御力、HPなどの初期値を管理する)などもそうで、
複数あるとした途端に面倒になります。こんなのも1つで良いです。

複数の初期値など必要とされていませんし、
こういうものは「複数個つくれるように」と意識した時点で実装コストがあがるので、再利用だとか部品化だとか考えずにちゃっちゃと書くべきです。
だいたい、必要となるパラメータなどちょくちょく変わってしまうのですから。
(無駄に汎用的にしようとして、失敗するケースはよくあります。自分もよくやります)

ところで上で言語処理系を例に出しましたが、インタプリタは、「複数生成されること」を前提にすべきものでしょうか? それともそうではないのでしょうか?
私は複数生成されることを前提にすべきだと思ったのでそういう設計にしました。
インタプリタともなるとさすがに作るのに手間がかかるので、「用途に応じて」というわけにはいきません。そういえばyacc/lexも、うかつに静的変数を持ち込んで複数使えなくなってしまった例ですね。

これもケースバイケースだと思います。
ゲーム内の言語処理系で、大それたことをしないのであれば自分が今までにつくったものは「複数生成されること」を意識していません。
これはこれで要求を満たせるので、複数の言語処理系が並列で動作する、ないしは別インスタンス(別空間)として動作する、
事を考えなくてよくなります。
経験上、インタプリタは「用途に応じて」よく作られます。勿論、ゲーム内における言語処理系なので、
きちんとしたプログラミング言語処理系ではないのですが、汎用的であればLuaなどを使えば良いので(といっても自分の中での実績はありませんが)
「用途にあわせて」組むのが普通だと考えています。
ゲームなどのスクリプトで「複数インスタンス」を前提としてしまうと、スクリプタ側の想定が大変になり、
しいてはデバッグも大変になりますし、使わない機能に実装コストを割く可能性すらあります。
なので、多少不自由でも「単一インスタンス」前提で考える「べきケースがある」と考えています。

結論としては、データが複数あるべきかどうかはドメインに依存する。これはオブジェクト指向には関係がない。
インスタンスが複数あることを意識し、オブジェクトに仕事をさせる事がオブジェクト指向ではない。

という考えになります。(勿論、これが普遍的な考え方だというつもりはありません)
自分は単にこう思うだけです。