質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

10回答

18579閲覧

アクセサにするべきかpublicにするべきか?

Chironian

総合スコア23272

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

2グッド

3クリップ

投稿2015/11/28 15:24

編集2015/12/02 09:12

すいません。まず、この話題はC++言語限定とさせて下さい。広げると収拾がつかなくなる予感がしますので。

下記のようなメンバ変数mMemberへのアクセス方法について、皆さんはどちらで定義しますか?
たぶんケースバイケースと思います。どんな時にprivate定義してアクセサを提供(Foo型)し、どんな時にpublic定義(Bar型)しますか?

C++

1class Foo 2{ 3private: 4 int mMember; 5public: 6 int getMember() const {return mMember;} 7 void setMember(int iMember) {mMember=iMember;} 89}; 10 11struct Bar 12{ 13 int mMember; 1415};

FooもBarと同様にmMemberを事実上全て開示してます。ですので、Foo型を用いるメリットはmMemberがアクセスされる際に何か処理を追加してもI/Fを変えないで済むことだけです。ですので、処理を追加する可能性がほぼ想定されないなら、無駄に複雑にしないためにBar型を使うべきです。
しかし、構造体(データを保持することが主目的)として設計したクラスであれば良いのですが、オブジェクト指向的に設計したクラスでは抵抗を感じてしまい、躊躇してます。
そこで、参考にさせて頂きたく、皆さんのご意見をお聞かせ下さい。

##結果報告
この議論を通じて、やっと自分なりの方針を明確にできました。
皆さん、ありがとうございます。

ベストアンサーは、C++らしい方針を最も明確に記載して頂けたcatsforepawさんにさせて頂きました。
また、「ブレーク貼れるようにアクセサ」という使い方に気づかせて頂けたこともありがたいです。

【検討のポイント】
私はシンプルイズベスト信者なので、メリットのない複雑さは悪と考えます。
なので、メリットもないのにアクセサを設けるのは悪なわけです。
ということは、アクセサを設けるメリットは何か?がポイントになります。

なお、アクセスされたタイミングで何らかのアクションを行うようなケースはメリットもなにも必須事項なので検討不要ですね。
また、そのような機能の追加も含め、何らかの変更が予想されるケースでは、アクセサを設けるメリットが明確ですので、こちらも検討不要です。
変更が予想されない時でも、アクセサ化するメリットがあるのか?が検討のポイントとなります。

【アクセサを設けるメリットのまとめ】
皆さんの意見のうち、【検討のポイント】的にメリットが明確なものを選択させて頂き、私が理解した内容で纏めてみました。(多少違っているかも知れません。その時はごめんなさい。)

raccyさん
メンバ変数へのアクセスは原則Read Onlyとするので、常にアクセサを使う。

私的な見解:
この基準(コーディング規約かも?)により、多少不慣れな人がいても比較的安全に開発を進めることができると言うメリットがあると感じます。
大勢の人がメンテナンスするようなプロジェクトで特に有用ですね。

catsforepawさんとyohhoyさん
変更が予想されないとはいえ可能性は0ではないので、アクセス時の処理変更の影響範囲が大きい場合、その影響を軽減するためにアクセサを設ける。
また、多くの場所からアクセスされる時はアクセサを設けるとデバッグしやすい。

私的な見解:
逆に、変更しても影響範囲が狭い場合はアクセサを設けるメリットが小さいのでpublicのままでも十分ということです。
そして、C++の場合は比較的熟練したプログラマが担当するケースも少なくないと思います。そのようなケースでは密結合部分についてpublicとして開示した方が、見通しも良くなりポインタ経由アクセス等、使えるテクニックも増えるのでより好ましいと感じます。

【私的な方針のまとめ】
私自身が担当しているプロジェクトは小規模・少数精鋭型が多いです。
そこで、下記方針を選択することにしました。

  • メンバ変数が少しの場所(数カ所程度?)からしかアクセスされないものについては、publicとする。
  • 外部に公開する部分も含め、これに該当しない場合はアクセサ化する。

あくまでも方針なのでそれなりに例外もあるとは思いますが、これで罪悪感を感じず(笑)にpublic定義できます。

######ところで、何故にC++限定だったのか?
最初に「この話題はC++言語限定とさせて下さい。広げると収拾がつかなくなる予感がしますので。」と記載させて頂きました。いまいち明確に表現できてなかったのですが、今回の議論を通じてやっと言語化できました。

C++言語でのプログラミングでは、可読性やメンテナンス性も当然熟慮しますが、高速性もかなり重視しており、多少(場合によっては大きく)可読性/メンナナンス性を劣化させてでも速度を取る言語です。ポインタはその典型例と思います。更に参照とかconst参照とか右辺値参照とかもうパニック・レベルですね。まして、テンプレート・メタ・プログラミングと来た日にはorz。

この辺がスピードを犠牲にして可読性やメンテナンス性に注力しているJavaやC#のような言語と大きく使い方が異なる部分と思います。
その結果、アクセサを定義する/しないの判断基準もそれなりに異なっていることが予想できるため、C++での方針を形作りたい自分にとっては、言語を限定した方がよいということだったのです。

それでは、最後にもう一度、Stripeさんも含め、ご回答頂いた皆さんありがとうございました。

tanat, kamokamo👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答10

0

private原理主義者な私は、常にprivateにしています。

まずもって、クラスの設計の仕方からpublicにするという行程が欠けています。

  1. いいクラス名を考える。
  2. クラスが提供するメソッドを考える。(C++であろうとメンバー関数などという言葉は使わない!)
  3. メソッドを実現するためにはどのようなデータを内部に持つ必要があるかを考える。この内部データはメンバー変数として実装するが、外には見せないことが前提、つまりはprivateにする。
  4. 他クラスとの連携で、内部データをそのまま渡した方がスムーズに行く場合のみ、getterを作ることを考える。
  5. 他クラスとの連携で、内部データをピンポイントで書き換える事が実装上どうしても必要な場合は、本当に必要なのか脳内会議を開いて、それでも必要と結論ができたときのみ、setterをしぶしぶ作ることを考える。

これがいいのかは私にはわかりませんが、たぶん、プログラミングを始めた頃にJavaの色んな本でpublicは悪と教え込まれ、その後にRubyに洗脳されてしまったからだと思います。C++をはじめからやっておけば、違ったやり方をしていたのかも知れません。

なお、構造体は全く別枠で考えています。

投稿2015/11/30 12:45

raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2015/11/30 13:54

なるほど、なんかカッコいいですね。 そっか、デフォルトがRead Onlyなのですね。まずはアクセス制限から入るならアクセサが必要になります。であれば、多少の複雑化は許容できそうですね。 多くの人がメンテナンスするケースで有用な方針のように感じます。 私自身は、内部的に閉じた部分ではコーディング時の制約が少ない方を取ることが多いです。C++ですから、やはりスピード命的な部分があるので、ここでコピーが走らないか?とか、この処理コンパイル時にできないか?とかを結構考えています。なので、内部的に閉じた部分ではあまりRead Onlyにはしないです。 外部へ公開する部分は、想定外の使い方を制限しておきたいので、不要な操作をできないように設計しますけど。
TaroToyotomi

2015/12/01 15:15

やっぱり、open-closed原則から考えてもそれがベターですよね。 >C++ですから、やはりスピード命的な部分があるので のようにヘッダファイルにsetter/getterを書いてinline展開するぐらいなら最初からpublicでいいじゃんっていう考え方もあるんですよね。
Chironian

2015/12/02 04:25

> 最初からpublicでいいじゃんっていう考え方もあるんですよね。 速度を上げるために無駄なコピーを防ぐことに注力したいから、Ready Onlyに煩わされたくないって感じです。内部的に閉じたクラスなら、それほど厳密にしなくても問題は起きにくいだろうと思ってます。 また、C++で書かれたプログラムを不慣れなプログラマがメンテするケースって比較的少ないと思うのですよ。C++を使いこなせるようになるのってそれなりに経験を積まないと厳しいですから。 ある程度の熟練者が対象なら、アクセス制限するより、密結合部のアクセス自由度は高い方がよいかなとも思ってます。
guest

0

ベストアンサー

例に挙げているような単純なアクセサは、作った方が良いと思われる積極的な理由がなければ、わざわざ作ったりはしません。内部的な(使用箇所が限定されるような)クラスでは作らないことが多いです。

ライブラリや共通部品として公開するような使用範囲の広いクラスでは、基本的には以下のような理由からアクセサを作ります。

  • デバッグがしやすくなる(テストコードを書いたり、ブレークポイントを置いたり)
  • セーフティガード(ポインタ経由で想定外の場所からメンバ変数を読み書きされないように)
  • アクセス時の処理を変更した場合の影響範囲の広さを考慮

投稿2015/11/29 00:51

catsforepaw

総合スコア5938

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2015/11/29 02:52 編集

ありがとうございます。 > 例に挙げているような単純なアクセサは、(中略)作らないことが多いです。 なるほど。私もそれが正しい姿と思うのですよ。背中を押して貰えてありがたいです。 > デバッグがしやすくなる(テストコードを書いたり、ブレークポイントを置いたり) おお、そういえばそうですね。この可能性は気がついてませんでした。 C言語時代に多用してたアドレスブレークに気を取られていたようです。 多くの場所からアクセスされるような変数の場合はアクセサ経由にしておくとデバッグに便利そうですね。 同時に処理追加時の影響も軽減できますし。 > セーフティガード(ポインタ経由で想定外の場所からメンバ変数を読み書きされないように) なるほど。アクセサを使うことでアドレスを隠せますね。
guest

0

>構造体(データを保持することが主目的)として設計したクラスであれば良いのですが
と述べているようにそのような設計であれば構造体でいいんじゃないですか?

メソッドの追加が必要になるということは再設計になるので、たとえばBar構造体をメンバに含むクラスFooを新しく作ればいいだけだと思います。

投稿2015/11/28 17:31

TaroToyotomi

総合スコア1430

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2015/11/29 01:54

すいません。質問が分かりにくかったようですね。 オブジェクトとして設計したクラスにpublicデータメンバーを設けるのは妥当か?否か?なのです。 それは良くないと書かれている書籍(Effectiv C++)もありますし、そうしない人も少なくないと思います。私もなんとなくそうしない人です。たぶん、Effective C++の影響もそれなりに受けていると思います。 しかし、冷静に考えるとオブジェクトとして設計されたクラスにもpublicデータメンバを設けた方が良いケースがある筈なので、他の方の方針を聞いてみたいのです。
TaroToyotomi

2015/11/29 12:20

あっそういうことですか。 自分の場合も内部的に特定の場所で使う(特定のオブジェクトのみからのみ参照)ならばアクセサは作らないと思います。 ただ、同じインスタンスが複数のオブジェクトから参照される可能性があるならばアクセサは一応作ります。後で排他制御とか入れるの大変ですので。
Chironian

2015/11/29 12:43

ありがとうございます。 使われる範囲の狭い/広いは構判断基準として重要そうですね。 > ただ、同じインスタンスが複数のオブジェクトから参照される可能性があるならば > アクセサは一応作ります。後で排他制御とか入れるの大変ですので。 なるほど。 マルチスレッドを想定している時は、比較的呼び出される場所が少ない時でもアクセサ経由を考えておいた方がよさそうですね。
Stripe

2015/11/29 16:08

ちなみに、そのアクセッサに対する排他制御は具体的にどのような実装の仕方をするんですか?
Chironian

2015/11/29 17:18

例えば、C++11ならstd::unique_lock<std::mutex>を使うと簡単です。 排他制御したい処理ブロックの先頭でunique_lockするだけですね。 std::mutex mMutex; int getMember() {   std::unique_lock<std::mutex> lock(mMutex);   //  排他制御したい追加処理   return mMember; }
Stripe

2015/11/29 22:38

別に排他制御そのものをやり方を質問しているわけではありません。 排他制御するためにアクセッサを実装するならば、それはどんな排他制御なのか?という意味です。 TaroToyotomiさんは、アクセッサとは関係なく、排他制御したいところで排他制御しますという話をしているわけではないと思うのですが。
Chironian

2015/11/30 01:18

> 排他制御するためにアクセッサを実装するならば、それはどんな排他制御なのか? それはその時になってみないと分かりません。 当「質問」は、アクセサを使う場合・publicを使う場合について皆さんの「方針」をお伺いしているので、「排他制御する可能性があるならアクセサ」は十分にその「方針」の1つになっていると思います。 それ以上の詳細は不要と考えます。
TaroToyotomi

2015/11/30 13:49 編集

int型とかの単純な変数をひとつだけRead&Writeする場合は排他制御は特にいらないかも知れませんが、1アクセスで閉じない場合は排他制御は必要だと考えています。 例えば複素数型や多倍長整数などがメンバだったらどうでしょう?。 →これはアクセサ(オペレータ)がないとアクセスできない不適切な例でした。 また、ある処理を行っている間はメンバを書き換えて欲しくないとか要求が後から発生した場合、変更がクラス内で済むのであれば十分なメリットだと思います。(こんな要求はめったに無いですが)
退会済みユーザー

退会済みユーザー

2015/11/30 21:40

taroさんの例ですが、リングバッファのデータカウントがそれだと思いついやのですが、けっこうありますよね。
Stripe

2015/12/01 12:21

> ある処理を行っている間はメンバを書き換えて欲しくない...変更がクラス内で済む それってつまりこういうことですか? class Hoge { private: int value; public: int getValue() { return value; } void setValue(int v) { lock(this); value = v; unlock(this); } void run() { lock(this); // valueを使った何らかの処理 unlock(this); } }; でも、これだと、 void thread1() { Hoge obj = getObj(); obj.setValue(123); obj.run(); } void thread2() { Hoge obj = getObj(); obj.setValue(456); obj.run(); } この2つをマルチスレッドで動かした時に破綻すると思います。 > リングバッファのデータカウント それって、そもそもアクセッサになるんですか?違うのでは?
TaroToyotomi

2015/12/01 13:50

>この2つをマルチスレッドで動かした時に破綻すると思います。 run()メソッド内でvalueの値が変わらないので目的は達成しているのでは? >それって、そもそもアクセッサになるんですか?違うのでは? リングバッファはあくまでも一例で、質問の主題はメンバをpublicにするかアクセサを作るかということなので違わないような気がします。 質問の主題から逸れてるので排他制御の話題は止めませんか?
guest

0

publicメンバは、constの場合のみですね…
「アクセサを用意する」という方針以外考えられません。

理由はいろいろ考えられますが、いくつか書いてみると…

・mMemberの型を変更する可能性
・mMemberの正当性をチェックしたい
・暗黙の型変換を防ぐ

#Fooがconstである場合、例のままだと読み出しアクセスさえできませんね。

C++

1 int getMember() const { return mMember; }

投稿2015/12/01 05:30

vga640x480

総合スコア13

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2015/12/01 08:23 編集

回答ありがとうございます。 > publicメンバは、constの場合のみですね… なるほど。実はこの質問を発したきっかけが、内部的に閉じたクラスのconstメンバは流石にアクセサ要らんよなって感じてたことでした。 ところで、公開するクラスでもconstメンバはgetを用意しないことってありますか? > 暗黙の型変換を防ぐ これってどうやるのですか? > Fooがconstである場合、例のままだと読み出しアクセスさえできませんね。 あっ、そうですね。時々やっちゃいます。ご指摘ありがとうございます。
hsk

2015/12/02 01:52

> 暗黙の型変換を防ぐ vga640x480さんの仰る内容を私なりに勝手に解釈すると... キャストをせず、それぞれの型に変換済みの値を返すアクセッサ(get)を用意するとの趣旨かと思いました。 class TimeStamp { private: double _value; public: double getValue() { return _value; } time_t getValue() { return ...; } SYSTEMTIME getValue() { return ...; } ... //もしくは operator double() { return ...; } operator time_t() { return ...; } operator SYSTEMTIME() { return ...; } }
Chironian

2015/12/02 02:28

hskさん、情報ありがとうございます。 しかし、やはりよく分かりません。 TimeStamp ts(...); float value = ts.getValue(); で暗黙の型変換されると思います。 仮に_valueをpublicで宣言していた場合も float value = ts._value; で全く同じ暗黙の型変換が発生するように思うのです。 C++って本当に奥が深いので、私が知らない/気がついていない何かのような気がするのですが、それがよく判らなくて。
hsk

2015/12/02 03:42 編集

確かに、プリミティブな型の事前定義された代入元・先の組み合わせや、標準的なライブラリで提供される変数型のそれであれば、受け取り側の型に合わせた暗黙的な変換が(まさに暗黙的に)行われますね。 アンチパターンみたいなものを示すことができればよいのですが、なかなか思いつかないとすると、結構曖昧か決め付けて的な動機なのかも...私。。 ご質問の >処理を追加する可能性がほぼ想定されないなら... により、クラス側も、クラスを利用する側も本当に起こらないならば、どちらでもよいのかもしれません。程度問題かと思います。 そうで無い場合(どちらかが変化するとき)、アクセッサを設けたほうが「楽」だなと思います(他者にコード保守を引き継ぐかもしれないですし...)が、受け取り側や中継時に変換関数を用意する方法もありますし、いずれにせよ、私の場合はメソッドを介させて、なるべく結合度を疎にさせるようにしています。 しかし、アンチパターンのようなものを示せればよいのですが、すぐに思い浮かばないとすると、そのあたり結構曖昧か決めつけ的なのかな...私も。。
guest

0

今現在、読み書きで使うのならpublicなメンバにして、アクセサの必要が生じた時点で、privateにしてアクセサに変更すれば宜しいかと思います。
将来の仕様変更においても、コンパイラが修正場所を漏れなくエラーメッセージとして報告してくれますから、人間の注意力に頼らない非常に安全な修正ができるはずです。
私は、積もり積もった複雑さ、猥雑化(コードの読みにくさを)の方を問題にします。

C++

1class int_acc { 2private: 3 int aaaa; 4public: 5#if 1 6 int& operator=(int i) { 7 this->aaaa = i; 8 return this->aaaa; 9 } 10#endif 11 operator int(){ 12 return this->aaaa; 13 } 14}; 15class cfoo { 16public: 17 int_acc abc; 18}; 19void bbbb() 20{ 21 cfoo a; 22 int i,j,k; 23 a.abc = 3; 24 i = a.abc; 25}

これはアクセサ?

投稿2015/11/29 02:34

編集2015/11/30 12:52
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2015/11/29 02:39

> 私は、積もり積もった複雑さ、猥雑化(コードの読みにくさを)の方を問題にします。 ですよね。私もそれが本来の姿と思うので、背中を押して欲しいという気持ちが強いです。
catsforepaw

2015/11/29 02:50

> アクセサの必要が生じた時点で、privateにしてアクセサに変更 限定された範囲での利用であればそれでかまわないと思います。私もよくやります。ただ、私の回答に書いた三つ目の理由になりますが、他の人も利用するクラスの場合、I/Fの変更には慎重でなくてはなりません。 > コンパイラが修正場所を漏れなくエラーメッセージとして報告してくれますから、 そして、「どうなっているんだ」という問い合わせのメールが届くことになります。
Chironian

2015/11/29 04:44

> そして、「どうなっているんだ」という問い合わせのメールが届くことになります。 あっはっは。その通りですね。 多くの人に使って貰う部分についてはアクセサ経由しておいた方が安全そうですね。
退会済みユーザー

退会済みユーザー

2015/11/29 04:52

そいつに、先週送信したメールを再送信。 そして、嫌気がさした設計者は、private原理主義になるか、public合理主義に。 ついに、高い技術力も暗黒面に・・・ なんて事にならにようにしたいですね。
catsforepaw

2015/11/29 05:08

I/Fを変更したらその旨を各チーム(担当者)に通知して修正依頼を出します。担当者は面倒だと思いながらも修正し、テストします。当然コストが発生します。一人二人ならともかく、人数が増えたらそのコストは無視できないものとなります。もし担当者が100人いて修正作業に30分かかるとしたら、50時間のコストが発生することになります。そんな余計なコストを発生させる可能性があるなら、最初からそうならないように設計するのが設計者の仕事です。○○主義などどうでも良い話です。
退会済みユーザー

退会済みユーザー

2015/11/29 09:40

catsforepawさんの「問い合わせ」の件は冗談ではなかったんですね。 >そして、「どうなっているんだ」という問い合わせのメールが届くことになります。 人間のエラーを機械が発見してくれたわけで、良い例ですよね? あとはプロジェクトの管理の問題なので、これを悪い例とした議論を『プロジェクト管理』の場で行ってください。 修正の量からしたって、   修正範囲が多い -->重要なのでアクセサにするべきだ。              コストが掛かるのでそのままにするべきだ。   修正範囲が少ないーー>コストが少ないのでアクセサにするべきだ。              使っているところが少ないのでそのままにするべきだ。 判断基準になりません。 >最初からそうならないように設計するのが設計者の仕事 そうは言っても変更はありますから、ここは厳密にならなくても良いと思います。 「問い合わせ」の件について、はぐらかした返答ですみません。たいへん失礼しました。
Stripe

2015/11/29 16:25

それで話は戻りますが、publicなメンバにアクセッサの必要が生じるケースというのは、具体的に言ってどんなケースですか?
Chironian

2015/11/30 01:28

> publicなメンバにアクセッサの必要が生じるケース このケースに言及しているのは、Stripeさんだけです。 また、Stripeさんは、アクセサを使う場合、publicを使う場合について特に方針をお持ちではないということですので、このStripeさんのコメントはスルーさせて頂きます。
退会済みユーザー

退会済みユーザー

2015/11/30 12:53 編集

実は私はアクセサの必要性をあまり感じていません。 追加したソースはアクセサでしょうか? そもそもアクセサって何でしょう?
Chironian

2015/11/30 15:09

代入演算子とキャスト演算子ですね。これらはアクセサとは言わないです。 確かにこの例ではメンバ変数をアクセスしてますが、この方式ですと複数のメンバ変数に対応できないからです。 アクセサを使うメリットですが、例えば、私の例で、int mMember;をdouble mMember;へ変更したとします。 そして、例えば、int getMember() {return round(mMember);}としておくことで、getMember()を使っている他のクラス側は少なくとも形式的には変更する必要がありません。 更に、真の意味で変更不要であることまでテストしてからリリースすれば、他のクラスの開発者は何もしないのでよいので、非常に助かります。 もしも、Fooが非常に汎用なライブラリで、幸いにも数万人の方に使って頂けていたような場合、その数万人がバージョンアップ時にプログラム修正不要なので、非常に効果的なのです。
退会済みユーザー

退会済みユーザー

2015/11/30 21:46 編集

Chironianさんがこうだ!って考えているアクセサってどんなものですか? 初心者質問者さんにはこんなこと聞きませんが、Chironianさんなら答えられる気がします。 そしてその答えに、この議論での答えがあるのではないでしょうか? 自分の意見を言わないのはズルイので申し上げますが、私は、 C++にはアクセサなんてなんて無いのだけれど他の言語からの概念を真似しててアクセサっぽいものも実装できるだけなんじゃないかと考え出しました。
Chironian

2015/12/01 02:11

あまり厳密ではないですが、ざっくりメンバ変数に対して1対1で読出/書込する関数をアクセサと呼んでます。 > そしてその答えに、この議論での答えがあるのではないでしょうか? なるほど。ちょっと考えてみました。 > C++にはアクセサなんてなんて無いのだけれど他の言語からの概念を真似しててアクセサっぽいものも実装できるだけなんじゃないかと考え出しました。 アクセサはオブジェクト指向プログラミングでコードを書く時の特定のパターンです。 C++はオブジェクト指向プログラミングに対応していますので、アクセサを書けますし、書くことが推奨されるケースがあります。 オブジェクト指向プログラミングでは内部の細かい実装を隠して、外部とは特定の関数を使ってI/Fするバターンが良く使われます。というか、内部の細かい実装を開示するべきではないので関数でI/Fするべきと言う主張をよく聞きます。かなり多くのケースで正しい主張と思います。 しかし、特定の変更要求や問い合わせについて、メンバ変数をそのままやり取りすれば十分ケースがあります。そのような時でもアクセサを使うべきか?について議論の余地があると思うのです。 今回の議論を通じてなんとなく感じているのですが、恐らくオブジェクト指向的に設計したクラスといえども、構造体のようにデータを保持する役割を果たすケースがあり、その際にもオブジェクトとしての指針を適用するのが妥当なのか?ってことかも知れません。 外部I/Fするクラスについては、「オブジェクト指向と構造体が入り混じったクラス」を設計するとやばい予感がします。オブジェクトの内部データを他の人が自由に触れるのはやはり危険な気がします。 しかし、内部的に閉じたクラスではこれを許した方が生産性をあげつつ、高速なプログラムを書けるのではないかと感じてます。 ああ、そう考えると、内部的に閉じたクラスは全部privateとし、相互friend指定するのも1つかも? ちょっと極端すぎるので穴が一杯ありそうですが。
guest

0

C++に限った話ではありませんが、構造体を使いたければ構造体を定義すればいいだけだし、オブジェクトを使いたければオブジェクトを定義するだけです。
結局のところ、あなたは何をしたいんですか?

投稿2015/11/28 16:09

Stripe

総合スコア2183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2015/11/29 01:56

すいません。質問が分かりにくかったようですね。 TaroToytomiさんへのコメントをご参照下さい。
Stripe

2015/11/29 16:03

> ...オブジェクトとして設計されたクラスにもpublicデータメンバを設けた方が良いケースがある筈なので... その根拠は?
Chironian

2015/11/29 16:59

> その根拠は? アクセサを設けるメリット(隠蔽)がpublicにするメリット(単純さ)を超えないケースが存在することです。 既にこのスレッドで上げられているように狭い範囲で使われるクラスの場合が上げられます。C++が使われる分野では良くあることです。 Stripeさんは「ある筈の根拠」をお尋ねになりましたので、逆にC++の分野において「ある筈」がない根拠をご提示頂ければ幸いです。
Stripe

2015/11/29 17:15

そもそもアクセッサのメリットは隠蔽ではありません。隠蔽したいなら、そもそもアクセッサを作りませんから。 また、アクセス範囲の狭い/広いは判断基準にはなりません。 そもそも、今回の質問で話題としている「メンバ変数(プロパティ)」は、どんな内容の変数なのでしょう? オブジェクト指向というなら、そのクラスにはプロパティ以外にメソッドもあるはずです。 結局のところ、あなたが何を作ろうとしているのかが重要になります。
Chironian

2015/11/29 18:08

アクセサのメリットは内部の実装を変更しても外部へ影響しないことなので、内部の実装の隠蔽と理解しています。 例えば、double mMember;やstring mMember;へ変更しても、それを隠して従来通りint型でI/Fできることがメリットと理解しております。 > 結局のところ、あなたが何を作ろうとしているのかが重要になります。 私の質問の主旨は、どんな時にアクセサを使い、どんな時にpublicデータメンバを使うかについて方針をお持ちの方に、その方針をお伺いしたいのです。 私が作ろうとしているものがなんであれ、皆さんの方針に影響することはないですので、私が何を作ろうとしているのかは重要ではありません。 Stripeさんが、publicデータメンバを定義することがお有りでしたら、それがどんな時なのか教えて頂けると幸いです。
Stripe

2015/11/29 22:52

あなたも既に分かっていると思いますが、ケースバイケースです。 例えば、あなたが言うように、もともとint型のプロパティをstring型に変更しても、int型のプロパティを提供し続けたいなら、アクセッサにせざるをえません。 特に方針とかはなく、アクセッサを実装する必要性が発生した時にアクセッサを実装します。
Chironian

2015/11/30 01:20

ご回答、ありがとうございました。 私の質問が方針を尋ねていることに疑義は無いと思います。 方針をお持ちでないのであれば、スルーして頂けると良かったです。
Stripe

2015/11/30 09:46

あなたの質問が方針を尋ねていることに疑義があります。 既に他の回答者から出ている、 「排他制御する時に、アクセッサを実装する」 「アクセッサの必要が生じた時に、アクセッサを実装する」 というのは、方針ではありません。 ケースバイケースの一例を示しているものだと思います。 それをあなたが、「方針」と言っているのです。 方針というのは、例えば、 「全てのプロパティの実装方式をアクセッサに統一することとする」 というようなものを言うのだと思います。 しかし、今回あなたは、状況に応じて「publicなメンバ変数」と「アクセッサ」を使い分けようとしていますよね? その、"状況に応じて使い分ける"っていうのは、ようするにケースバイケースということなんじゃないですか? ケースバイケースな話なのですから、最終的に「あなたが何をしたいのか?何を作ろうとしているのか?」という所に集約されるのだろうと思います。
Chironian

2015/11/30 11:03

なるほど。Stripeさんは、ケースバイケースなら具体的な状況が分からないと分類できないとお考えなのですね。 私はそうは思いません。抽象的な状況でも分類できる方針が存在し得ると考えています。その方針をお持ちの方に、それを教えて欲しいと質問させて頂きました。 この点で決定的に考え方が異なりますので、お互いにスルーするしかないと思います。
Stripe

2015/11/30 14:14

その「抽象的な状況でも分類できる方針」のことを、あなたは既に知っているんじゃないんですか? しかし、それは「抽象的な方針」であったため、けっきょく具体的なところがよく分からないという状況なのだと思います。
guest

0

こんにちは。もしかしてすでに似た趣旨のご回答をされている方もおられるかもしれませんが...

C++では、クラスのメンバーにおいてアクセス修飾子を指定しない場合 private になりますので、クラスについて基本的にはメンバーを隠匿するような使用法を言語設計者は意図しているのだろうと思います。
私の経験的にですが、vga640x480さんが仰られる内容や、デバッグのしやすさなどから、アクセッサを経由させたほうが保守性が向上しています(改修などで、あとから楽)。
たとえば、メンバー変数の型のほうを変更した場合も、公開する変数型を同じままに保つことができます。

オブジェクトやデータをさらけ出してひとかたまりとして扱いたい場合は、既定のアクセスレベルがpublicである、構造体を私なら使用します(class から structにコードを変えます)。

ご質問にもどりますと、私なら躊躇せずメンバー変数は隠匿し、アクセッサを定義します。すべて公開したいデータ(オブジェクトや変数)の集まりなら、構造体にします。すぐ捨てるサンプル的なコードならともかく、面倒ですがクラスを定義すると同時(早い段階)でアクセッサも用意します。

余談ですが、C++でも.NET Frameworkのマネージドアプリケーションを作れますけれども、オブジェクト間のバインド機構を使うときにプロパティ(アクセッサ)である必要がある(フィールド(メンバー変数)は使用不可)だったりするので、 .NET 開発者もclassオブジェクトに関しては同様の考えを持っているのかな...と感じます。いちいちアクセッサを書くことは面倒かもしれませんが、IDEがずいぶん進化していて助けられます。

趣旨からずれていたらすみません。

投稿2015/12/02 01:40

編集2015/12/02 02:06
hsk

総合スコア728

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2015/12/02 03:07 編集

ご回答ありがとうございます。 > メンバー変数は隠匿し、アクセッサを定義します。 私もどちらかと言うとそうするのですが、変更の可能性がほとんど考えられないような場合にアクセサを導入することによるソースの複雑化を正当化できるのはどんな時なのか? という疑問なのです。 私自身はシンプルイズベストと考えています。つまり、無意味な複雑化は害悪と思うので、何らかの複雑さを導入する場合は、そのメリットが必要と思います。 なのに私自身、この方針に反して、将来的な変更が考えられないようなメンバ変数をpublicにすることに躊躇してしまうのです。 そこで、他の方のご意見も参考にさせて頂いて、自分なりの方針を固めたいと思っています。 多くの方のご意見を頂き、かつ、議論にお付き合い頂き、だんだん見えてきています。 どうもC++言語のマルチパラダイムな部分も影響しているような気がしてます。 私なりの方針がある程度まとまったら、報告させて頂こうと思います。
guest

0

ベストプラクティスパターン
(ケント・ベック著)
という本がありまして、
本で使用している言語がC++
ではないので申し訳ないのですが、
インスタンス変数を直接アクセス
すべきなのか、それともメソッドを
必ず介すのがいいのかという
議論が紹介されていました。

どちらが良いとか悪いとかではなく、
どちらも一長一短があり、
難しい議論です。ます。

お読みになってみては
いかがでしょうか?

蛇足ながら、
私個人はこの本を読了し、
必ずメソッドを介したほうが
良いと結論を出しました。

投稿2015/12/01 11:23

umeaji

総合スコア101

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2015/12/01 12:45

情報ありがとうございます。 > ベストプラクティスパターン SmallTalkの本なのですね。ばりばりのオブジェクト指向っぼいですね。 その世界でも、「どちらが良いとか悪いとかではなく、どちらも一長一短があり、難しい議論です。」というのはちょっと驚きです。オブジェクト指向的には、private一択と思ってました。
guest

0

構造体(データを保持することが主目的)として設計したクラスであれば良いのですが、オブジェクト指向的に設計したクラスでは抵抗を感じてしまい、躊躇してます。

個人的には、言及されているものとおおよそ同じ指針をとっています。使い分けの線引きラインとしては、「責任分界の強度」を用いています。外部仕様のようにインタフェースがはっきりとしている場合には、厳密な使い分けを心がけますが、内部実装のように結合度が高い場合には、曖昧な使い分けになります。

ついでにstructとclassの使い分けもすることが多いです。言語仕様的にはデフォルトでpublic/privateという差しかありませんが、コンパイラでは無く人(プログラマ)に意味を伝える目的で使い分けています。


単なるデータの集まりを表す場合は、publicメンバをもつstructとして宣言します。基本的にメンバ関数は実装しません。

  • データコンテナ型のようにある程度複雑なデータ構造を表現する場合、データメンバを直接privateで公開せずに隠蔽し、より高位の操作をアクセサとしてpublicメンバ関数化することがあります。
  • ライブラリ内部実装向け入れ子(Inner)クラス等では、publicメンバ+実装上便利なメンバ関数を実装することがあります。

いわゆるオブジェクト指向的なクラスの場合は、全て非publicメンバをもつclassとして宣言します。データメンバへは必ずアクセサ経由とします。

  • 上記繰り返しとなりますがライブラリ内部実装向けのクラス等では、(手抜きのため)一部をpublicメンバ化することもあります。

余談ですが、アクセサのメリットとして挙がっている、排他制御(スレッドセーフ性)の後付けはおすすめしません。あるクラスがスレッドセーフ操作を提供するか否かは、決して内部の実装詳細などではなく、そのクラス外部仕様の一部であるべきです。後付けでの排他制御実装は論理破綻、もしくは実行時の不安定動作リスクに繋がります。

投稿2015/11/30 02:37

yohhoy

総合スコア6191

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2015/11/30 04:16

ご回答、ありがとうございます。 >外部仕様のようにインタフェースがはっきりとしている場合には、厳密な使い分けを心がけますが、内部実装のように結合度が高い場合には、曖昧な使い分けになります。 了解です。yohhoyさんも内部実装ではpublicデータメンバも有りなのですね。 多くの人が同様な方針のようでホッとしています。 > 単なるデータの集まりを表す場合は、publicメンバをもつstructとして宣言します。基本的にメンバ関数は実装しません。 (後略) 積極的にstructを使い分けている人が複数いるって心強いです。 昔、あるJavaウィザードの人がstructの概念を理解してくれず、データを保持することが目的のクラスにメンバ関数を入れないことを納得してくれず苦労したことがあります。 たぶん、C++とJavaの成り立ちの相違が原因かなと感じてます。 > 排他制御(スレッドセーフ性)の後付けはおすすめしません。 全くその通りですね。排他制御は設計的に対応しておかないと、再現性が非常に低いのでマジ辛いことになりますから。 マルチスレッド対応のライブラリの場合は、必要な部分は最初から排他制御しておくべきと私も思います。 外部的にはマルチスレッド非対応で内部的に高速化等のためにマルチスレッド化するようなケースでは、内部I/Fで閉じると思います。過去、そのようなケースを担当したことがあります。 その時も、基本設計的には自社側プログラムで対応できるように設計しておき、使用する他社ライブラリがスレッド安全ならばそちらに任せ、そうでない時はこちらで対応しました。 当時はまだC++をベターCとして使っていたのでアクセサ云々は意識してませんでしたけどね。
yohhoy

2015/11/30 08:38 編集

確かにJavaメインの人には「アクセサ以外あり得ない」と言われそうですね… 考え方はJavaBeansあたりから来ているような気もします(適当) Java言語圏のgetter/setter文化については http://d.hatena.ne.jp/Nagise/20141010/1412930502 などが面白いです。 対象のプログラミング言語が違えば思想もちがってくるので、その辺は柔軟に対応してほしい/すべきではありますが、実際にはなかなか難しいかも。
Chironian

2015/11/30 09:52

> 確かにJavaメインの人には「アクセサ以外あり得ない」と言われそうですね ですね。JavaとC++では同じオブジェクト指向でも雰囲気というか文化の相違を感じます。 情報ありがとうございます。URL先の文書、読み応えありますね。 IDEがGUI部品を取り扱えるようにするための仕様がJavaBeansで、それがアクセサを要求するのですね。なるほど。
guest

0

私の場合クラスと構造体の使い分けは「データ保持以外に機能を持たせるかどうか」という点です。

データ保持用のために設計した場合、構造体として定義してメンバが特殊でどうしても初期化処理が必要な場合はコンストラクタを書くこともありますが、基本的にはメンバ関数は持たせません。

データ保持以外になんらかの演算をさせたい場合、クラスとして設計してメンバ変数を隠蔽します。単純なアクセサも書きますが、get/setともに、範囲チェックなどの処理も含めたものにすることが多いです。

投稿2015/11/28 22:06

KoichiSugiyama

総合スコア3041

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2015/11/29 01:45

> どうしても初期化処理が必要な場合はコンストラクタを書くこともありますが、基本的にはメンバ関数は持たせません。 私も同じ方針です。std::vector<>に入れる時等、コンストラクタだけは必要になりますからね。 > データ保持以外になんらかの演算をさせたい場合、クラスとして設計してメンバ変数を隠蔽します。 そうなんですよ。私もなんとなくそのようにしてしまいます。 でも、積極的な理由もないのに単純なアクセサを設けるのは、シンプルイズベスト原則に照らすと宜しくないのでちょっと悩ましく感じてます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問