
【設計思想的な質問】
自分は、結合が弱いソースを好んでおり、
以下の例で言えば、後者のようにするのですが、
このようは書き方は良くないのでしょうか?
新しい現場で、後者の方の書き方をしたら、
「えっ、なんでメンバー変数で宣言してんの? なんで引数で渡してんの?馬鹿じゃないの?」
と、結構、傷つく言葉を受けました。
それはそうとして、
自分としては、「なぜ、やってはいけないのか?」
「自分の設計思想は、設計の方向性としてアリなのか?」などを知りたく、質問させて頂きました。
ご意見、ご鞭撻を宜しくお願い致します。
//-----------------------------------------------------------
★前提1:末端の派生クラスで、このクラスを継承することはない。
★前提2:提供する処理は様々な処理で使われるユーティリティ機能
◆案1
class 複雑なクラス
//メンバ変数
変数1
変数2
(中略)
変数100
//メソッド
private
long 複雑な処理 (void)
(略:色んなメンバー変数が登場するが、
どのメンバと、どのメンバが登場するかは、ソースを追いかけて確認する)
long 自由なタイミングで、色んなメンバ変数の値を書き換えてしまうメソッド (void)
(※上述の「複雑な処理」に登場する変数の取りうる値がフリーダムになる)
となるよりも、
//-----------------------------------------------------------
◆案2
static long 複雑な処理 (必要な引数1、必要な引数2、必要な引数3)
(略:登場するINPUT、OUTPUTは、引数に列挙されているものだけ。)
static long 複雑な処理 (必要な引数97、必要な引数98、必要な引数99)
(略:登場するINPUT、OUTPUTは、引数に列挙されているものだけ。)
※渡す値は、呼び元が意識するため、メンバ変数がどういう状況になっているかは、
メソッドの利用者が把握している。(タイミングも、取りうる値も、認識しやすい)
の方が良いという考えなのです。
メンバ変数で書けば、確かに、宣言部、定義部のコーディングで引数のことを書かなくてよいので楽でしょうし、
インタフェースの検討不足で手戻りがあっても、引数の増減で、修正が不要ですし、楽だと思います。
しかし、
「どこからでもアクセスできる変数ばかり」だと、処理の認識も困難だし、バグの温床にもなってしまうことから、私は後者にも、優れている部分はあると考えているのです。
.
No.6ベストアンサー
- 回答日時:
そもそもの設計思想が間違ってる
オブジェクト指向のプログラミングとは
例えば、クルマという製品を完成させる場合
エンジンというクラス
ハンドルというクラス
電気系統というクラス
シャーシというクラス
タイヤというクラス
他いろいろあると思うが
これらのクラスを最終的に取りまとめれば良い
細かく言うと、さらにエンジンというクラスの中にも
シリンダーというクラスがあったり点火プラグと
いうクラスがあったりして、最終的にエンジン
というクラスを取りまとめれば良い
最終的なプログラマの仕事は、
無いクラスだけ設計、作成したら
これらのクラスを取りまとめる部分だけを行うのが
理想的な設計
案1は、いきなりクルマというクラスを作ってるような物
案2は単なるライブラリ
クルマを作るうえで必用な材料と工具を用意しましたよ
と言っているだけの話
案1、案2のどちらの言い分が良いかではなく
どちらも悪い
ありがとうございます。
> 案1は、いきなりクルマというクラスを作ってるような物
>
> 案2は単なるライブラリ
> クルマを作るうえで必用な材料と工具を用意しましたよ
> と言っているだけの話
この言葉、すごく納得しました。
案1、案2のどちらも、イケてないですね。
案1は、エンジン、ハンドル、タイヤに分解すべきだし、
案2は、走る。止まる。向きを変える。などと「可能な処理の羅列」にしかなっていないので、例えば、
「運転手続き」といった制御系メソッドというか管理系メソッドというか、
(1)ドアを開ける
(2)キーを回す
(3)エンジンをかける
(4)アクセルを踏む
(5)ハンドルをきる
(6)ブレーキを踏む
のようなものが必要。
(バラバラすぎて何をしたいのかがわからない)
No.7
- 回答日時:
「対立としてはわかりやすい」ってのは, 単に
どっちも「クラスをどう設計/実装するか」という視点だから比較することも理解できる
ってだけです. 「一方ではクラスを作っているのに他方では作っていない」というのに比べれば, ね.
ただ, 「メンバ変数が入る余地がないように、static宣言をつけたい」というのはちょっと違和感があります. クラスに対して static なメンバ関数を作るということだから, そのメンバ関数はそのクラスと「なんらかの関係がある」ということですよね. 「メンバ変数には一切アクセスしない, だけどそのクラスとは関係がある」って状況がどのくらい適切なのかというと.... すぐには想像できないなぁ.
もちろん「static なメンバ変数にはアクセスする」ということなら「static なメンバ関数」であることもわかるんだけど.
ありがとうございます
適切にクラス分割できたケースでは、staticにもせず、メンバ変数も使いたいと思います。
(プロジェクトに途中から参画する場合も多いので、自分でクラス分割できないケースでは難しそうですが)
◆自分の結論
各クラスを小さくし、凝集性を高くし、「機能」と「データ」が切り離せない(どのメソッドでも「あるメンバー変数」が登場するくらいまでになっている。)
くらいまで適切にクラス分割し、
staticにもせず、メソッド間もメンバー変数を介してインタフェースにしようと思います!
No.5
- 回答日時:
>案1
>long 自由なタイミングで、色んなメンバ変数の値を書き換えてしまうメソッド (void)
>
>
>案2
>※渡す値は、呼び元が意識するため、メンバ変数がどういう状況になっているかは、
>メソッドの利用者が把握している。
>
この時点でおかしい、
long 自由なタイミングで、色んなメンバ変数の値を書き換えてしまうメソッド (void)
を使用する前に
呼び元が意識するためメンバ変数がどういう状況になっているかを返すステータス関数
を用意すればよい話
自由なタイミングで色んなメンバ変数の値を書き換える必要性があるのなら
その部分をしっかり設計するのが普通のSEの仕事でしょ
プロセスの同期処理や排他制御をちゃんと行ってこその設計ではないでしょうか
ありがとうございます。
> 呼び元が意識するためメンバ変数がどういう状況になっているかを返す
> ステータス関数を用意
自由をコントロールすべきですね。
No.4
- 回答日時:
質問文を読んでの個人的な感想は #1 に近いんだけど, それ以前に「そもそもこの 2つの案がどう対立しているのか」がよくわからない. 例えば
案1 では「class」と書かれているが案2 には書かれていない
わけですよ. とすると, 素直に読めば「案2 ではクラスとしてまとめず個々の関数がばらばらに入っている」と読める. ただ, そうだとすると
・その状況で (当該翻訳単位からのみ呼び出せることを指示する) static を指定すると何がうれしいのか
・なぜ案1 では「クラス」としてまとめたのか
が読み取れない.
案2 も「クラスの内部に static なメンバ関数を置く」ということを表しているのだとすれば対立としては分かりやすい (ただしその場合には案2 の方で「class」と書かない理由が分からない) ものの
・#1 でも指摘されているように, そもそもクラス設計がおかしいのではないか (「ユーティリティ機能」とあいまいにしているところがあやしい)
・案2 の「INPUT」や「OUTPUT」の意味が分からない: 本当にそれらしか使わないなら, そもそも static なメンバ関数であるは必要あるのか?
という疑問は持ってしまう.
Tacosanさん、
回答ありがとうございます。
いつも、とても助かっています。
> 「そもそもこの 2つの案がどう対立しているのか」がよくわからない.
> 案2 には「class」と書かれていない
⇒ すみません、書き忘れです。大変申し訳ないです。。
(でも、もし書いていたとしても、
#1さんにご教授して頂いたように、NameSpaceで関数群を作成すべきですが)
なので、
> 「クラスの内部に static なメンバ関数を置く」ということを表しているのだとすれば対立としては分かりやすい
の解釈をして頂ければと思います。
>「INPUT」や「OUTPUT」の意味が分からない: 本当にそれらしか使わないなら,
> そもそも static なメンバ関数であるは必要あるのか?
自分の意図、狙い、メリットは、
【何をINPUTとして、どんなOUTPUTを出すか、
引数を見ればわかりやすくなる。】 というところです。
逆の例の方がわかりやすいかもですが、
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
引数なしで、そのメソッド内に、色んなメンバ変数が登場し、
それらのメンバ変数のうち、
「値の更新を含まず、情報源(INPUT)としてのみ使用するもの、
「値の更新がされうるもの」が混在しているのが、すごく嫌なのです。
自分は、
関数名 ( const p_In_あれ
const p_In_これ
p_Out_それ* )
のように「何と何を情報の材料とし、何の情報として出力するか?」
を明確にしつつ、メンバ変数が入る余地がないように、
static宣言をつけたいのです。
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
No.3
- 回答日時:
案2の、引数100個のメソッドとかはあり得ないです。
引数の型が全て String だったとすると、『呼出で51個目と
52個目が逆だった』みたいなバグを探すのが滅茶苦茶
大変です。引数が2桁になるようになった段階で、『引数と
なるデータをまとめるコンテナクラス』を作ることを検討
すべきでしょう。
例えば、直線の座標を渡す場合
・メソッド名(始点X座標、始点Y座標、終点X座標、終点Y座標)
とするのではなく、『直線の始点終点』を格納するクラスを
作って
・メソッド名(『直線の始点終点』)
と呼出引数をまとめたほうが可読性が上がることも多いでしょう。
メソッド内で値を変更されても、引数に渡したオブジェクト内の
話なのですっきりします。
案1が良いかどうかはクラスの粒度によります。
案1で複雑な処理をする場合は、そのクラスがその処理に特化
している場合に限られると思います
(初期化→メンバ変数セット→メソッド実行→結果取得→破棄
のようにオブジェクトの寿命が短い場合です)
案1は慎重に設計しないと『オモチャ箱 (意味も考えずに何でも
間でもデータが突っ込まれてしまう)』になってグチャグチャに
なりやすいので慎重に取り扱う必要があります。前述のような
処理特化クラス以外では、私としてはあまり推奨できません。
No.2
- 回答日時:
明らかに案2のほうが優れた設計だと思います。
関数がstaticなのは、メンバに直接アクセスしないという意図を表しているということだと思いますが、
実際にjavaでリファクタを使用したときにそのような構造であれば自動でstatic関数になりますし、
元々案1の構造だったのを、案2のように改善した、ということであれば非常に良いと思います。
ただし、新たなコードを書いて案1,2のようになった、というのであれば両方ともあまりよくありません。
class シンプルなクラス
オブジェクト1
オブジェクト2
オブジェクト3
シンプルな処理(オブジェクト...)
自分で簡単に出来ることはやる
そうでないことは他のオブジェクトにお願いする
最近の普通の設計は大体こんな感じで、各要素は出来る限り簡単にして、自分で出来ないことは
他にお願いする、という形にするのが普通です。
そもそも、メンバ変数が多すぎてメソッドがメンバをどのように扱うかはっきりしないような構造は
間違いです。
状況にもよるとは思いますが、最近では1関数が20行あれば大きい関数と判断されることも多いと思います。
長巨大クラス内でメンバ変数の制御が出来なくなった→ひとまず案2のように変数のコントロールを改善する
というのは良いのですが、クラスの分離が良く出来ている場合、各メソッドがメンバを直接参照するのが
問題になることは殆どありません。
ありがとうございます。
自分も、シンプルなクラスの集合体にすべきと考えます。
また、機能を凝集させ、
機能Aを持ったクラスXは、
ある機能Bを実行したい場合、機能Bに特化したクラスに処理をお願いするべきと考えます。
> 長巨大クラス内でメンバ変数の制御が出来なくなった
> → ひとまず案2のように変数のコントロールを改善する
> というのは良いのですが、
> クラスの分離が良く出来ている場合、
> 各メソッドがメンバを直接参照するのが
> 問題になることは殆どありません。
確かに。
クラスの持つ、
「機能」と「データ(メンバ変数)」の関わり合いが、密であれば密であるほど、
どのメソッドからでもメンバ変数にアクセスするのは自然な気はします。
例えば、
車が、ハンドル、エンジンをhasしているとき、
ハンドルクラス : 車(タイヤ?)が向いている方向 というメンバ変数
エンジンクラス : 現在出力している馬力 というメンバ変数
自分は少し固執していたかもしれません。
(※まぁ、巨大すぎるクラスは、かなりの高確率で大抵、分解できるとは思っていますがw)
No.1
- 回答日時:
うーん、なんというかどっちもどっちだなぁ……
というのが(質問者様には失礼ですが)この質問を見たときの偽らざる感想です。
まずそもそもの前提として、案1のように全ての機能を一つのクラスに組み込むと多数のメンバ変数と複雑な処理が必要となるならば、そこには何らかの内部構造があるはずです。したがって、その内部構造を分析しクラス構造として抽出することが可能かつ取り回しが容易になるはずです。
案1ですが、上記のように改善の余地が多分にあるよう見受けられ、その意味ではあなたがその方法を取らなかったのは正しい選択と思います。
というか、「なんで引数で渡してんの?」と言う方が「馬鹿じゃないの?」と言いたくなります。
しかし案2にも「……えーと?」と思う点があります。
それは static 識別子です。
もしこの関数がユーティリテイクラスの外からは見せない処理であるためにソースの方にだけ書いてあるのならばある程度は問題ではないとは思います。無論、外部からの関数隠蔽には無名 namespace というもっと適切な記述方法がありますし、static 関数が増殖してクラス化したほうがいいのにそうしていないなら問題ですが。
しかし、その関数がメンバ関数としてユーティリティクラスに含まれているならば問題がある恐れがあります。
つねにクラスを基礎に書かなくてはならない Java 等と違って、C++ ではクラスの外に関数を書くことができます。つまり、クラスインスタンスの内部に直接アクセスする必要がある特別な場合を除いてクラスメソッドを使う意味があまりないと私は考えます。このときグローバル関数を作りたくない、関連はあるけどクラスにまとめると関数しか並ばないというのならば namespace が使えます。
また、Java で言う private static メソッドを使いたいならば上記の無名 namespace が使えます。
まとめると、案1はクラス設計上問題がある、案2はクラス実装上問題がある可能性がある、そもそもユーティリティ機能に細分化すべきクラス構造が隠れていないか検討が必要、というのが自分の考えです。
ただ、これは具体的な状況が質問に含まれていないためあて推測が含まれています。なんにせよほかの開発者との意見のすり合わせが必要かと思います。その時必要なのは、あなたが対話する相手は戦友であって敵ではなく、対話する場所はソフトウェアの完成という一つの目的のために作業する場であってバトルロイヤル会場ではないということを心に留めておく事です。
……などというと「何を偉そうに」と思うかもしれません。しかし、これは自分がやってしまった過去の反省から書いていることなのです(しかもプロジェクト採用面接で……当然落ちましたとも。なんでああなったんだろうなぁorz)。
回答ありがとうございます。
> 案2にも「……えーと?」と思う点があります。
> それは static 識別子です。
今後は、
(1)ある機能として方向性を持った関数群
⇒ クラスにせずに、NameSpaceで関数群を作る。
(2)private static的な関数群
⇒ C++では、private static メソッドを作れないので、
無名 namespaceで作る。
のようにしていこうと思います。
丁寧な言葉を選んで頂き、ありがとうございます。
(人間性の豊かさを感じます。)
心遣い、痛み入ります!
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- その他(プログラミング・Web制作) pythonのプログラムについての質問です。 1 2023/05/26 10:31
- その他(プログラミング・Web制作) どういうプログラムで組みますか?google colabでやってるんですけど、出来る方お願いします。 1 2022/07/17 18:41
- Java java 飾子を付けること(public static・・・) ・コンソールへの出力処理はmainメ 2 2022/06/16 19:34
- その他(プログラミング・Web制作) どういうプログラムで組みますか?google colabでやってるんですけど、出来る方お願いします。 1 2022/07/06 09:28
- その他(プログラミング・Web制作) このプログラミングをどう組みますか? Googlecolabでやってるんですが、出来る方お願いします 1 2022/07/13 10:52
- Ruby 初心者プログラミング 3 2022/10/12 11:31
- C言語・C++・C# C言語初心者 ポインタについて、お助けください、、 2 2023/03/15 23:50
- 数学 モデルのパラメータの定義がいまいちわかりません。 3 2022/10/11 15:16
- Java javaの質問です 次の機能を有するメソッド4つを自クラスに作成し、実装したいです 【機能】 足し算 1 2022/06/15 17:49
- JavaScript カラーミーショップのsectionループ内で、[引数][戻り値]ありの関数的な処理を行いたいです。 1 2022/05/07 19:39
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
「継承されたメソッドの可視性...
-
C# 「データが失なわれる可能性...
-
Javaのインスタンス化の構文の...
-
interface,extend,implementの...
-
VB DLLプロジェクトについて
-
バックグラウンドでキー監視
-
【C#】クラスのコンストラクタ...
-
デバッグ時に「Source not found」
-
Calendar型を使用して日付の差...
-
JTextFieldの入力制限
-
Javaでは多重継承ができない、...
-
「ラッパークラス」の存在意義...
-
WPF C# PointToClient
-
InputStreamのread()関数
-
インターフェースとトレイトっ...
-
委譲って何ですか?
-
private継承はどう使う?
-
メソッドの引数にクラス名を渡す
-
「タイプ初期化子が例外をスロ...
-
private static という変数の修飾
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
VB DLLプロジェクトについて
-
(vba)他のアプリケーションの右...
-
【C#】クラスのコンストラクタ...
-
メソッドの引数にクラス名を渡す
-
C# 「データが失なわれる可能性...
-
C#からDLLを呼びたいのですが・...
-
「IOException は対応する try ...
-
c++でのヘッダーファイルの循環...
-
ファイルパスが取得出来ない(P...
-
「ラッパークラス」の存在意義...
-
抽象クラスをJUNITでテストする...
-
ASP.NETでの共通コードの書き方...
-
interface,extend,implementの...
-
「ひとつのインターフェースで...
-
javanoリストのオブジェクト作...
-
オーバーライドとラッパーの違い
-
System.Collections.Generic.Li...
-
VBがオブジェクト指向言語でな...
-
compareToにおける「自然順序付...
-
Commons-Discovery.jarとは?
おすすめ情報