• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:【設計思想の質問】staticメソッド、メンバ変数)

設計思想的な質問の回答

このQ&Aのポイント
  • 結合が弱いソースを好む設計思想についての質問です。
  • メンバ変数や引数の使い方に関して意識がありますが、正しい設計思想かどうか知りたいです。
  • メンバ変数で書く場合と引数で操作する場合の利点と欠点についても考えています。

質問者が選んだベストアンサー

  • ベストアンサー
  • nak777r
  • ベストアンサー率36% (49/136)
回答No.6

そもそもの設計思想が間違ってる オブジェクト指向のプログラミングとは 例えば、クルマという製品を完成させる場合 エンジンというクラス ハンドルというクラス 電気系統というクラス シャーシというクラス タイヤというクラス 他いろいろあると思うが これらのクラスを最終的に取りまとめれば良い 細かく言うと、さらにエンジンというクラスの中にも シリンダーというクラスがあったり点火プラグと いうクラスがあったりして、最終的にエンジン というクラスを取りまとめれば良い 最終的なプログラマの仕事は、 無いクラスだけ設計、作成したら これらのクラスを取りまとめる部分だけを行うのが 理想的な設計 案1は、いきなりクルマというクラスを作ってるような物 案2は単なるライブラリ クルマを作るうえで必用な材料と工具を用意しましたよ と言っているだけの話 案1、案2のどちらの言い分が良いかではなく どちらも悪い

souken_200
質問者

お礼

ありがとうございます。 > 案1は、いきなりクルマというクラスを作ってるような物 > > 案2は単なるライブラリ > クルマを作るうえで必用な材料と工具を用意しましたよ > と言っているだけの話 この言葉、すごく納得しました。 案1、案2のどちらも、イケてないですね。 案1は、エンジン、ハンドル、タイヤに分解すべきだし、 案2は、走る。止まる。向きを変える。などと「可能な処理の羅列」にしかなっていないので、例えば、 「運転手続き」といった制御系メソッドというか管理系メソッドというか、 (1)ドアを開ける (2)キーを回す (3)エンジンをかける (4)アクセルを踏む (5)ハンドルをきる (6)ブレーキを踏む のようなものが必要。 (バラバラすぎて何をしたいのかがわからない)

その他の回答 (6)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.7

「対立としてはわかりやすい」ってのは, 単に どっちも「クラスをどう設計/実装するか」という視点だから比較することも理解できる ってだけです. 「一方ではクラスを作っているのに他方では作っていない」というのに比べれば, ね. ただ, 「メンバ変数が入る余地がないように、static宣言をつけたい」というのはちょっと違和感があります. クラスに対して static なメンバ関数を作るということだから, そのメンバ関数はそのクラスと「なんらかの関係がある」ということですよね. 「メンバ変数には一切アクセスしない, だけどそのクラスとは関係がある」って状況がどのくらい適切なのかというと.... すぐには想像できないなぁ. もちろん「static なメンバ変数にはアクセスする」ということなら「static なメンバ関数」であることもわかるんだけど.

souken_200
質問者

お礼

ありがとうございます 適切にクラス分割できたケースでは、staticにもせず、メンバ変数も使いたいと思います。 (プロジェクトに途中から参画する場合も多いので、自分でクラス分割できないケースでは難しそうですが) ◆自分の結論 各クラスを小さくし、凝集性を高くし、「機能」と「データ」が切り離せない(どのメソッドでも「あるメンバー変数」が登場するくらいまでになっている。) くらいまで適切にクラス分割し、 staticにもせず、メソッド間もメンバー変数を介してインタフェースにしようと思います!

  • nak777r
  • ベストアンサー率36% (49/136)
回答No.5

>案1 >long 自由なタイミングで、色んなメンバ変数の値を書き換えてしまうメソッド (void) > > >案2 >※渡す値は、呼び元が意識するため、メンバ変数がどういう状況になっているかは、 >メソッドの利用者が把握している。 > この時点でおかしい、 long 自由なタイミングで、色んなメンバ変数の値を書き換えてしまうメソッド (void) を使用する前に 呼び元が意識するためメンバ変数がどういう状況になっているかを返すステータス関数 を用意すればよい話 自由なタイミングで色んなメンバ変数の値を書き換える必要性があるのなら その部分をしっかり設計するのが普通のSEの仕事でしょ プロセスの同期処理や排他制御をちゃんと行ってこその設計ではないでしょうか

souken_200
質問者

お礼

ありがとうございます。 > 呼び元が意識するためメンバ変数がどういう状況になっているかを返す > ステータス関数を用意 自由をコントロールすべきですね。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

質問文を読んでの個人的な感想は #1 に近いんだけど, それ以前に「そもそもこの 2つの案がどう対立しているのか」がよくわからない. 例えば 案1 では「class」と書かれているが案2 には書かれていない わけですよ. とすると, 素直に読めば「案2 ではクラスとしてまとめず個々の関数がばらばらに入っている」と読める. ただ, そうだとすると ・その状況で (当該翻訳単位からのみ呼び出せることを指示する) static を指定すると何がうれしいのか ・なぜ案1 では「クラス」としてまとめたのか が読み取れない. 案2 も「クラスの内部に static なメンバ関数を置く」ということを表しているのだとすれば対立としては分かりやすい (ただしその場合には案2 の方で「class」と書かない理由が分からない) ものの ・#1 でも指摘されているように, そもそもクラス設計がおかしいのではないか (「ユーティリティ機能」とあいまいにしているところがあやしい) ・案2 の「INPUT」や「OUTPUT」の意味が分からない: 本当にそれらしか使わないなら, そもそも static なメンバ関数であるは必要あるのか? という疑問は持ってしまう.

souken_200
質問者

お礼

Tacosanさん、 回答ありがとうございます。 いつも、とても助かっています。 > 「そもそもこの 2つの案がどう対立しているのか」がよくわからない. > 案2 には「class」と書かれていない  ⇒ すみません、書き忘れです。大変申し訳ないです。。    (でも、もし書いていたとしても、     #1さんにご教授して頂いたように、NameSpaceで関数群を作成すべきですが) なので、 > 「クラスの内部に static なメンバ関数を置く」ということを表しているのだとすれば対立としては分かりやすい の解釈をして頂ければと思います。 >「INPUT」や「OUTPUT」の意味が分からない: 本当にそれらしか使わないなら, > そもそも static なメンバ関数であるは必要あるのか? 自分の意図、狙い、メリットは、 【何をINPUTとして、どんなOUTPUTを出すか、  引数を見ればわかりやすくなる。】 というところです。  逆の例の方がわかりやすいかもですが、 ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■  引数なしで、そのメソッド内に、色んなメンバ変数が登場し、  それらのメンバ変数のうち、  「値の更新を含まず、情報源(INPUT)としてのみ使用するもの、  「値の更新がされうるもの」が混在しているのが、すごく嫌なのです。  自分は、  関数名 ( const p_In_あれ       const p_In_これ        p_Out_それ* )  のように「何と何を情報の材料とし、何の情報として出力するか?」  を明確にしつつ、メンバ変数が入る余地がないように、  static宣言をつけたいのです。 ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

noname#212058
noname#212058
回答No.3

案2の、引数100個のメソッドとかはあり得ないです。 引数の型が全て String だったとすると、『呼出で51個目と 52個目が逆だった』みたいなバグを探すのが滅茶苦茶 大変です。引数が2桁になるようになった段階で、『引数と なるデータをまとめるコンテナクラス』を作ることを検討 すべきでしょう。 例えば、直線の座標を渡す場合  ・メソッド名(始点X座標、始点Y座標、終点X座標、終点Y座標) とするのではなく、『直線の始点終点』を格納するクラスを 作って  ・メソッド名(『直線の始点終点』) と呼出引数をまとめたほうが可読性が上がることも多いでしょう。 メソッド内で値を変更されても、引数に渡したオブジェクト内の 話なのですっきりします。 案1が良いかどうかはクラスの粒度によります。 案1で複雑な処理をする場合は、そのクラスがその処理に特化 している場合に限られると思います (初期化→メンバ変数セット→メソッド実行→結果取得→破棄 のようにオブジェクトの寿命が短い場合です) 案1は慎重に設計しないと『オモチャ箱 (意味も考えずに何でも 間でもデータが突っ込まれてしまう)』になってグチャグチャに なりやすいので慎重に取り扱う必要があります。前述のような 処理特化クラス以外では、私としてはあまり推奨できません。

souken_200
質問者

お礼

引数100の関数はさすがにあり得ないです! (案2をよく見て頂くと、実は、1メソッドあたり3個になっております。。)

  • qwertfk
  • ベストアンサー率67% (55/81)
回答No.2

明らかに案2のほうが優れた設計だと思います。 関数がstaticなのは、メンバに直接アクセスしないという意図を表しているということだと思いますが、 実際にjavaでリファクタを使用したときにそのような構造であれば自動でstatic関数になりますし、 元々案1の構造だったのを、案2のように改善した、ということであれば非常に良いと思います。 ただし、新たなコードを書いて案1,2のようになった、というのであれば両方ともあまりよくありません。 class シンプルなクラス  オブジェクト1  オブジェクト2  オブジェクト3  シンプルな処理(オブジェクト...)   自分で簡単に出来ることはやる   そうでないことは他のオブジェクトにお願いする 最近の普通の設計は大体こんな感じで、各要素は出来る限り簡単にして、自分で出来ないことは 他にお願いする、という形にするのが普通です。 そもそも、メンバ変数が多すぎてメソッドがメンバをどのように扱うかはっきりしないような構造は 間違いです。 状況にもよるとは思いますが、最近では1関数が20行あれば大きい関数と判断されることも多いと思います。 長巨大クラス内でメンバ変数の制御が出来なくなった→ひとまず案2のように変数のコントロールを改善する というのは良いのですが、クラスの分離が良く出来ている場合、各メソッドがメンバを直接参照するのが 問題になることは殆どありません。

souken_200
質問者

お礼

ありがとうございます。 自分も、シンプルなクラスの集合体にすべきと考えます。 また、機能を凝集させ、 機能Aを持ったクラスXは、 ある機能Bを実行したい場合、機能Bに特化したクラスに処理をお願いするべきと考えます。 > 長巨大クラス内でメンバ変数の制御が出来なくなった > → ひとまず案2のように変数のコントロールを改善する > というのは良いのですが、 > クラスの分離が良く出来ている場合、 > 各メソッドがメンバを直接参照するのが > 問題になることは殆どありません。 確かに。 クラスの持つ、 「機能」と「データ(メンバ変数)」の関わり合いが、密であれば密であるほど、 どのメソッドからでもメンバ変数にアクセスするのは自然な気はします。 例えば、 車が、ハンドル、エンジンをhasしているとき、 ハンドルクラス : 車(タイヤ?)が向いている方向 というメンバ変数 エンジンクラス : 現在出力している馬力 というメンバ変数 自分は少し固執していたかもしれません。 (※まぁ、巨大すぎるクラスは、かなりの高確率で大抵、分解できるとは思っていますがw)

  • hitomura
  • ベストアンサー率48% (325/664)
回答No.1

うーん、なんというかどっちもどっちだなぁ…… というのが(質問者様には失礼ですが)この質問を見たときの偽らざる感想です。 まずそもそもの前提として、案1のように全ての機能を一つのクラスに組み込むと多数のメンバ変数と複雑な処理が必要となるならば、そこには何らかの内部構造があるはずです。したがって、その内部構造を分析しクラス構造として抽出することが可能かつ取り回しが容易になるはずです。 案1ですが、上記のように改善の余地が多分にあるよう見受けられ、その意味ではあなたがその方法を取らなかったのは正しい選択と思います。 というか、「なんで引数で渡してんの?」と言う方が「馬鹿じゃないの?」と言いたくなります。 しかし案2にも「……えーと?」と思う点があります。 それは static 識別子です。 もしこの関数がユーティリテイクラスの外からは見せない処理であるためにソースの方にだけ書いてあるのならばある程度は問題ではないとは思います。無論、外部からの関数隠蔽には無名 namespace というもっと適切な記述方法がありますし、static 関数が増殖してクラス化したほうがいいのにそうしていないなら問題ですが。 しかし、その関数がメンバ関数としてユーティリティクラスに含まれているならば問題がある恐れがあります。 つねにクラスを基礎に書かなくてはならない Java 等と違って、C++ ではクラスの外に関数を書くことができます。つまり、クラスインスタンスの内部に直接アクセスする必要がある特別な場合を除いてクラスメソッドを使う意味があまりないと私は考えます。このときグローバル関数を作りたくない、関連はあるけどクラスにまとめると関数しか並ばないというのならば namespace が使えます。 また、Java で言う private static メソッドを使いたいならば上記の無名 namespace が使えます。 まとめると、案1はクラス設計上問題がある、案2はクラス実装上問題がある可能性がある、そもそもユーティリティ機能に細分化すべきクラス構造が隠れていないか検討が必要、というのが自分の考えです。 ただ、これは具体的な状況が質問に含まれていないためあて推測が含まれています。なんにせよほかの開発者との意見のすり合わせが必要かと思います。その時必要なのは、あなたが対話する相手は戦友であって敵ではなく、対話する場所はソフトウェアの完成という一つの目的のために作業する場であってバトルロイヤル会場ではないということを心に留めておく事です。 ……などというと「何を偉そうに」と思うかもしれません。しかし、これは自分がやってしまった過去の反省から書いていることなのです(しかもプロジェクト採用面接で……当然落ちましたとも。なんでああなったんだろうなぁorz)。

souken_200
質問者

お礼

回答ありがとうございます。 > 案2にも「……えーと?」と思う点があります。 > それは static 識別子です。 今後は、 (1)ある機能として方向性を持った関数群  ⇒ クラスにせずに、NameSpaceで関数群を作る。 (2)private static的な関数群  ⇒ C++では、private static メソッドを作れないので、    無名 namespaceで作る。 のようにしていこうと思います。 丁寧な言葉を選んで頂き、ありがとうございます。 (人間性の豊かさを感じます。) 心遣い、痛み入ります!