●属性と状態操作

状態操作は、状態取得操作、状態設定操作をまとめて言うときに用いています。


01212/01212 VFF15672 T's-Neko 属性と操作 (10) 97/02/01 07:07 01205へのコメント 小林 浩一 さん、こんばんは。T's-Neko です。(^o^)o | C++でもSetXxxやGetXxxをちまちま作るのはとっても面倒。 |いや、そんなクラスはどこか間違ってる!とは思うものの、実際は |そんなクラスがたくさん。困ったもんだ。 確かに、属性1つ1つに対して関数をいちいち書くのはメンドウですね。 しかし、property キーワードを使いたくはありません。(CLOS のアクセサも同 様。)というのは、変数の代入なのか関数の呼び出しなのかハッキリして欲しい からです。 そうしないと、関数呼び出しによる余計な副作用が起きるのを見つけることが難 しくなります。変数の代入や参照は、その変数以外は影響しないという保証があ るので、デバッグする際にここは影響が無いと断定できますが、副作用があると 仮定すると非常に厄介です。もちろん副作用が無いように作られていることが大 前提ですが、所詮、人間が作ったものですから絶対に保証できるものではありま せん。 その意味では、単に代入や参照するだけの、インラインの SetXxx や GetXxx も 使いたくありません。副作用があるかもしれないとみる部分が多くなってしまい ます。 また、チューニングする(パフォーマンスを上げる)のが非常に厄介になります。 関数呼び出しはコストがかかるので(インライン関数を除く)、なるべく関数呼 び出しを少なくすることでチューニングしますが、その判断できなくなります。 だから、ボクは、多少カプセル化が破られても参照専用でも、変数を public に します。今のところドキュメントに参照専用と書くだけなので多少不安です。将 来、クラスの使う側から変数の値を設定できないようにするキーワードができる といいのですが。(ANSI C++ の mutable がそうかな?>えぴさん) 少し視点は変わりますが、SetXxx や GetXxx が増えることはいいことだとボク は考えています。 たとえば、「方程式」クラスに「計算する」操作があるものと、「解を得る」操 作があるものを比べると、どちらかというと「解を得る」方がより具体的で操作 の内容がよく分かります。何故なら、「計算する」では何を計算するのか曖昧に なっているからです。では、「解を計算する」操作にすればいいかというと、そ うでもなく、その操作の出力に解があるかどうかがまだ曖昧です。 属性を抽出することでより詳しく分析したとも考えられますね。 SetXxx や GetXxx の Xxx は属性ですから、それは操作ではなく属性に分析され るべきですね。ですから、ボクの場合、ドキュメントには次のように書きます。 属性 ・(Xxx の日本語)[Xxx, SetXxx()] ... 設定だけ副作用がある場合 操作 (SetXxx などは書かない) __ T's-Neko __
01232/01233 VFF15672 T's-Neko RE^2:属性と操作 (10) 97/02/03 15:06 01213へのコメント TN さん、こんばんは。T's-Neko です。(^o^)o |>しかし、property キーワードを使いたくはありません。(CLOS のアクセサも同 |>様。)というのは、変数の代入なのか関数の呼び出しなのかハッキリして欲しい |>からです。 |>そうしないと、関数呼び出しによる余計な副作用が起きるのを見つけることが難 |>しくなります。変数の代入や参照は、その変数以外は影響しないという保証があ |>るので、デバッグする際にここは影響が無いと断定できますが、副作用があると | | これはどっちみち保証にはならないのでは? そもそもオブジェクトの |中の変数をいじくるという行為は,変数を参照するであろうそのオブジェクト |のふるまいに対して「何かが変わって欲しい」という意志の現れなはずだし。 | その瞬間にはなにも副作用がなくても,一時間後に別のメンバ関数を起動した |ら副作用がでた,なんてことになるんじゃないかと思います。read onlyな |public変数メンバ(C++にあったっけ?)指定すれば違うでしょうが。 もちろん、変数の値が変わることで、他のオブジェクトがそれを参照していれば、 後になって影響はあります。それは、置いておきましょう。 「影響しないという保証」は他のオブジェクトの振る舞いのことではなく、変数 の値のことです。デバッグする際、正規状態(バグの状態ではない状態)にある と判断できる最もプリミティブな要素は、変数の値と考えています。 ですから、たとえば、「人」オブジェクトに、[名前 name」属性と「誕生した 年 birthday_year」属性と「年齢 yearsOld」属性がある場合、 class Man { // 属性 char* name; // 名前 int birthday_year; // 誕生した年(参照専用) int yearsOld; // 年齢(参照専用) // 属性への操作 void setBirthday_year( int ); void setYearsOld( int ); }; void aMethod() { Man aMan; aMan.name = "Mac."; // ... (1) aMan.setBirthday_year( 1960 ); // ... (2) } (1) では、名前 name 属性以外の属性には影響しないことが言えますが、 (2) では、誕生した年 birthday_year 属性を設定したことにより、他の属性 (年齢 yearsOld 属性)が影響する可能性があります。(たぶんするでしょう) それが、隠蔽されてしまうと、 void aMethodUsedProperty() { Man aMan; aMan.name = "Mac."; // ... (3) aMan.birthday_year = 1960; // ... (4) } (4) で、誕生した年 birthday_year 属性と別の属性(年齢 yearsOld 属性)が 影響受けることは(クラスをよく知った人でなければ)予想が付きにくいです。 だから代入だけでも他の属性に影響があるかも知れないと仮定してしまうと、 (3) でもその仮定をし、変数の状態をウォッチしなければならないステップが増 えることになります。 つまり、誕生した年 birthday_year 属性と年齢 yearsOld 属性が、正規状態に あるかチェックする個所は (2) のような、操作(関数)になっている部分に 限定できる、というメリットがあるにも関わらず、property キーワードによっ て、そのメリットが消えてしまうということです。 このメリットと、可読性(演算子をつかうか関数を使うか)を比べると、この メリットの方がメリットが大きいと思います。関数は読みにくいけど、素人でも すぐにその意味(関数の内容)を理解できますからね。 余談ですけど、この、property 嫌いは、Visual Basic の Change イベントから 始まっています。単なる代入なのに、イベントによりメソッドが起動するのが わかるまで、デバッグに非常に苦労した経験があったんですよ。 |(余談) | Delphiのpropertyの場合,アクセス手段のバリエーションとか配列property |とか,ストリームがらみのいろいろな機能といった,いわゆる代入演算子の |オーバーロード以外の機能が多くて,わかった気になるのに数週間かかり |ました(^^; Set,Getなしでもproperty化するだけでいろんな機能が |出てくるし... Delphi は、開発環境と連携しているので、その property 化にいろんな機能と ルールができるのでしょうね(推測)。便利な反面、それは仕方ないと思います。 でも、ボクが property を使いたくない理由は、それではありませんよ。 ---- simple implement is best. T's-Neko
01233/01233 VFF15672 T's-Neko RE^2:属性と操作(続き) (10) 97/02/03 15:06 01219へのコメント 小林 浩一 さん、こんばんは。T's-Neko です。(^o^)o |>様。)というのは、変数の代入なのか関数の呼び出しなのかハッキリして欲しい |>からです。 | | むしろ区別なく利用できる方向に進んでいると思います。 |Eiffelなんかはその傾向が顕著ですね。 そうなんですよね。ボクは悲しい。 Java も演算子のオーバーロードが採用されるようですし。トホホ |>その意味では、単に代入や参照するだけの、インラインの SetXxx や GetXxx も |>使いたくありません。副作用があるかもしれないとみる部分が多くなってしまい |>ます。 | | SetXxxは副作用があると見る方が妥当な操作でしょう。ある属性が |変化したときに、連動して変化しなくてはいけないものがある場合も |あるでしょう。それをユーザから隠蔽できるように、メソッド経由で |アクセスするのは非常によいことだと思いますが。 メソッド経由でアクセスできるのはいいのですが、その連動する変化が あるかどうかまでユーザーに隠してしまうのは、どうかと思います。 単なる代入で、副作用があるかどうかを予想できる人は少ないと思いま すよ。 | GetXxxも、属性の内部表現をユーザに隠蔽できるので、直接メンバ変数を |外へ公開するよりはずっとよいと思います。 属性の内部表現を隠蔽する? 結局、返り値(出力)として出ると思いますが。 多分、そうじゃなくて、計算して得られるような冗長性のある属性を変数に するなという意味でしょうね。それは賛成です。 それを GetXxx にするまでは賛成ですが、property で代入演算子にするのは どうかと思うんです。(理由は前述と #1213 へのコメントを参照) |>また、チューニングする(パフォーマンスを上げる)のが非常に厄介になります。 |>関数呼び出しはコストがかかるので(インライン関数を除く)、なるべく関数呼 |>び出しを少なくすることでチューニングしますが、その判断できなくなります。 | | インラインにすればよいだけですよね? |関数呼び出しを減らしてまでインラインを避ける理由があるのですか? だから、「判断」ができなくなるんですよ。 インラインでも、関数形式()で書かれているので、もしかするとこいつの呼び出 しにコストが掛かっているんじゃないかと誤って判断してしまう可能性があると いうことです。その判断のために、いちいち関数定義まで調べなければならない ですからね。 |>そうしないと、関数呼び出しによる余計な副作用が起きるのを見つけることが難 |>しくなります。変数の代入や参照は、その変数以外は影響しないという保証があ |>るので、デバッグする際にここは影響が無いと断定できますが、副作用があると |>仮定すると非常に厄介です。もちろん副作用が無いように作られていることが大 |>前提ですが、所詮、人間が作ったものですから絶対に保証できるものではありま |>せん。 | | C++では、constメソッドは(意味的には)副作用が無いと考えてよいことに |なります。 |>だから、ボクは、多少カプセル化が破られても参照専用でも、変数を public に |>します。今のところドキュメントに参照専用と書くだけなので多少不安です。将 |>来、クラスの使う側から変数の値を設定できないようにするキーワードができる |>といいのですが。(ANSI C++ の mutable がそうかな?>えぴさん) | | mutableは、constメソッドからも更新できるメンバ変数に指定します。 |先に、constメソッドは「意味的には」副作用がない、と書きましたが、 |意味的には変わらないけれども、内部表現が変化するような場合に |活用できます。たとえばなんらかのキャッシュを内蔵しているような |クラスとか。 mutable は、そういうものでしたか。ありがとうございます。 でも、意味的に副作用が無いだけでは駄目なんですよ。人間ですから。 |>少し視点は変わりますが、SetXxx や GetXxx が増えることはいいことだとボク |>は考えています。 | | たとえばメンバ変数すべてについてSetXxxとGetXxxがあり、それ以外の |メソッドが一つも無かったらどうですか? それって構造体に そうですね。 ボクはインライン関数の出現で、喜びと同時に、小林さんの言うような構造体に 毛が生えたクラスが増えると感じました。 でも、ボクは、単に SetXxx や GetXxx が増えることはいいことだと書いたつも りはないんです。操作を更に分析して属性を見つけようと言っているんです。 (#1212参照) | そこまで極端ではなくても、SetXxx,GetXxxが多いクラスというのは、 |自立していない、抽象度の低いクラスである可能性が高く、その意味で |「どこか間違っている」んじゃないかと感じられてしょうがない。 |でも、そんなクラスが多いんですよね。まだまだ勉強。 OWL クラスライブラリの SetXxx, GetXxx に相当する操作は、そう思いますね。 (MFC は勉強中。) p.s.: VC++ を学割で買ってしまった。同じ値段で TC++ があったけどね。 ---- simple implement is best. T's-Neko しまった。入門レベルじゃないぞ。
01248/01249 VFF15672 T's-Neko RE^4:属性と操作(続き) (10) 97/02/04 11:14 01237へのコメント 小林 浩一 さん、こんばんは。T's-Neko です。(^o^)o | なんだか話がかみ合わない感じですね。 そうですね。(^^; |デバッグ時にしたって、メンバ変数をあちこちから更新されると、 |不正な更新がされた場合に、どこでその不正な更新がされたかを |見つけるのが困難です。大規模なシステムでは、手に負えなくなります。 |(デバッガで、メモリが更新されたらブレークってのもあるけど異常に遅い) |関数にしてあればSetXxx()にブレークポイントつけるだけ。 なるほど。そういう手があったか。 | ひょっとして、クラス(それも他人の作ったクラス)のユーザとしての |立場からクラスそのものをデバッグしようとしてません? |だから副作用の有無を「クラスのユーザ(Nekoさんね)の責任」でコントロール |したがってるのでは? そんなのは「クラスの責任」だと思うんですけど。 はい。その立場です。 デバッグ時にどのクラスの責任(バグの原因)かを調べるのに有効だと思うんで すが。 | おっしゃってることがよく分かりませんが、インラインにできる程度の |関数のオーバーヘッドなんて、ほとんどの場合パフォーマンスに影響無いと |思いますが。効果のないパフォーマンスチューニングやってません? あまり、効果は期待できないかもしれませんが、コードを書く最中に瞬間的に チューニングできるところが気に入っています。 | それより、関数にしておいた方がエラーチェックがちゃんとできる |(こっちの方がはるかに重要!)し、内部表現が変わった場合でもユーザには |それを隠蔽できるし、メリットは多いと思うけどなぁ。 内部表現が変わった場合に隠蔽できる、というのがよく分からないのですが、 具体的に教えていただけませんか。 ---- simple implement is best. T's-Neko
01248/01249 VFF15672 T's-Neko RE^4:属性と操作(続き) (10) 97/02/04 11:14 01237へのコメント 小林 浩一 さん、こんばんは。T's-Neko です。(^o^)o | なんだか話がかみ合わない感じですね。 そうですね。(^^; |デバッグ時にしたって、メンバ変数をあちこちから更新されると、 |不正な更新がされた場合に、どこでその不正な更新がされたかを |見つけるのが困難です。大規模なシステムでは、手に負えなくなります。 |(デバッガで、メモリが更新されたらブレークってのもあるけど異常に遅い) |関数にしてあればSetXxx()にブレークポイントつけるだけ。 なるほど。そういう手があったか。 | ひょっとして、クラス(それも他人の作ったクラス)のユーザとしての |立場からクラスそのものをデバッグしようとしてません? |だから副作用の有無を「クラスのユーザ(Nekoさんね)の責任」でコントロール |したがってるのでは? そんなのは「クラスの責任」だと思うんですけど。 はい。その立場です。 デバッグ時にどのクラスの責任(バグの原因)かを調べるのに有効だと思うんで すが。 | おっしゃってることがよく分かりませんが、インラインにできる程度の |関数のオーバーヘッドなんて、ほとんどの場合パフォーマンスに影響無いと |思いますが。効果のないパフォーマンスチューニングやってません? あまり、効果は期待できないかもしれませんが、コードを書く最中に瞬間的に チューニングできるところが気に入っています。 | それより、関数にしておいた方がエラーチェックがちゃんとできる |(こっちの方がはるかに重要!)し、内部表現が変わった場合でもユーザには |それを隠蔽できるし、メリットは多いと思うけどなぁ。 内部表現が変わった場合に隠蔽できる、というのがよく分からないのですが、 具体的に教えていただけませんか。 ---- simple implement is best. T's-Neko
01257/01257 VFF15672 T's-Neko ★read only public と profiler を是非! (10) 97/02/05 12:59 01252へのコメント 小林 浩一 さん、こんばんは。T's-Neko です。(^o^)o |>| なんだか話がかみ合わない感じですね。 |> |>そうですね。(^^; | | ははは。(^^; ま、気を取り直して。 ま、よくあることだ〜ね。(^^; 気にしない、気にしない。 (一応、これでも分かるように説明しているつもりですけどね) |>| ひょっとして、クラス(それも他人の作ったクラス)のユーザとしての |>|立場からクラスそのものをデバッグしようとしてません? |>はい。その立場です。 | | (^^;;;;; |こ、これはもしかして、「他人の作ったものを信じられない症候群」を |発症しておられますね! まぁ、出来のいいプログラマにはよくあるのかな。 |でもねー、何年もすると、使われるクラスも使うクラスもどっちとも |他人(それも若手!)が作ったもので、Nekoさんは全体がちゃんと動くように |目を見張らせてる、なんてことになったりするはずです。そうなると、 |見方も変わるんじゃないカナー。 その症候群は抜けないですね。ボクは90%他人の作ったプログラムをさわって いますが、現に間違いがあるんですもん。(^^; というより、実際は、事前条件がドキュメントされていなかったり、事前条件の チェックがしてなかったりが非常に多いので、どちらの責任か曖昧なところなん ですが、やっぱり、使われている側(ユーザーであるボクではない)の責任だと 思います。 ところで、「全体に目を見張らせる」ためには、操作の扱っているクラス(属性、 メンバ変数、ローカル変数)のユーザとしての立場と、その操作を属している デバッグ中のクラスのデベロッパーとしての立場の両面を持っていると思います。 で、ボクは、扱っているクラス(属性、メンバ変数、ローカル変数)のユーザと しての立場の意識が強いですね。それらのオブジェクト同士の妥当性を確かめな がらデバッグします。 | メンバ変数をpublicにして、だれからでも自由に更新できるように |した場合、ある更新でクラスの状態が正しいことを、だれが保証できます? |いくつかのユーザがいくつかあるメンバ変数をばらばらに更新して不正な |(矛盾した)状態になったとき、それはだれの責任? うー、read only public な変数が欲しい〜! でも、ドキュメントにするだけでも、事前条件を確かめることと同じことと 思うので、かなり妥当性を保証できると思いますよ。(実際、ボク自身は、 昔に作ったものでもそのテのミスはしたことないのですが、他人はどうか 知らないので断言できないぃ) |>| それより、関数にしておいた方がエラーチェックがちゃんとできる |>|(こっちの方がはるかに重要!)し、内部表現が変わった場合でもユーザには |>|それを隠蔽できるし、メリットは多いと思うけどなぁ。 |> |>内部表現が変わった場合に隠蔽できる、というのがよく分からないのですが、 |>具体的に教えていただけませんか。 | | メンバ変数の型を変更しても、GetXxxなりSetXxxなりが型変換をすれば、 |クラスのユーザには影響無いですよね、という意味。 |よくある(?)例題は、座標(Point)を表すクラスが、x座標とy座標をメンバ |変数で持っていて、SetX()とかGetY()とかあったんだけど、都合があって(^^; |メンバ変数を角度と長さで持つようになっても、SetX()なんかが頑張れば |それを使ってるユーザは困りませんよね、という感じ。 | なんかクラスを知った最初期のメリットってこういうのだったような。 | | 事前条件、事後条件、クラス不変表明、ついでにx,yと角度と長さの例題 |(って、本当は内部で両方の表現を持つんだけど)も含めて、興味がある方は | |「オブジェクト指向入門」 |Bertrand Meyer著/二木厚吉監訳、酒匂寛・酒匂順子 訳 |アスキー出版局 ISBN4-7561-0050-3 |4980円 | |をどうぞ。この本の原題が「Object Oriented Software Construction」 |略してOOSCで、この第2版が#1225あたりで話題になっているものです。 なるほど、ありがとうございました。(入門らしいぞ(^^)) しかし、内部表現の変更はあまりしない方がいいですよね。 VFAT(長いファイル名の格納の仕組み)でも、従来の FAT と内部構造が 変わってないのは、互換性を維持するためですね。従来と同じ UI です、 と Win99 が大々的に宣伝しても、そのためには FAT32 システムに更新する 必要があります、っていわれたらたまりませんもんね。 データベース(#9-1795)でも同じですね。 でも、小林さんの仕事では、内部変更がしょっちゅう有るみたいですね。 ご苦労様です。(他人ごと)(^^; そうそう、STL では、演算子のオーバーロードを使わないと出来ませんね。 うーん、やっぱりそうなるのかな。 は、そうか! 将来、read only public も採用されて、property も採用されれば、 クラスの妥当性を保証できるし、インターフェイス(書式)は変わら ないから隠蔽できるし、属性ベースの STL も使えるし、見やすいし、 メンドウな GetXxx を作らなくて済むし、もう完璧ですね。 ということで、ANSI 標準委員会に連絡とれる方、お願いします。 たとえば、 class XYPoint { read_only_public: // 変数に対して read only, 関数には public int x; // x 座標を得る int y; // y 座標を得る int property_read r(); // 長さを得る void property_write r(int); // 長さを設定する int property_read p(); // 角度を得る void property_write p(int); // 角度を設定する // property 関数は static にできない。... 関数ポインタは得られない void aMethod(); // 操作、メンバ関数(後述) }; void foo() // クラスの外の操作 { Point p; p.r = 100; p.p = 120; // クラスの外からは、property_write 関数を cout << p.r << p.p << endl; // 同じく property_read 関数を呼び出す cout << p.x << p.y << endl; // read only 変数を参照 p.x = 100; p.y = 120; // read only だからエラー // ただし、property_write x() 関数があればそれを呼び出す。 int R = p.r = 100; // property_read 関数の後 property_write 関数を int* ip = &p.x; // エラー、read only では、アドレス参照できない int* ip2 = &p.r; // エラー、property_read 関数も、同様 } デバッガには、property read 関数も(オプションで)表示 ----------------------------- p --+ +-- x : 50 +-- y : 88 +-- r : 100 +-- p : 120 ----------------------------- あと、スコープの問題があるな。 void XYPoint::aMethod() // クラスの中の操作 { x = 50; // クラスの中からは、変数を property_write x(100); // 明示的に property write 関数を呼ぶ } 抽象クラスもできますね。 class Point { public: virtual int property_read x(); // x 座標を得る virtual void property_write x(int); // x 座標を設定する virtual int property_read y(); // y 座標を得る virtual void property_write y(int); // y 座標を設定する virtual int property_read r(); // 長さを得る virtual void property_write r(int); // 長さを設定する virtual int property_read p(); // 角度を得る virtual void property_write p(int); // 角度を設定する }; これは、 class SubPoint : public Point { public: int x, y, r, p; }; でも通るようにして欲しいですね。 あとは、デバッガにこの行に副作用(操作の呼び出しやイベントの発生)が あるかすぐに判るようにしたいな。profiler などが表示してくれれば、 ボクのしたい瞬間的チューニングもできますね。 ということで、デベロッパーの方、お願いします。 たとえば、 ----------------------------- void foo() // クラスの外の操作 { Point p; * p.x = 100; p.y = 200; // クラスの外からは、property write 関数を * cout << p.r << p.p << endl; // 同じく property read 関数を } ----------------------------- ↑副作用(関数の呼び出しやイベントの発生)があるという '*' 表示 p.s.:結局、ボクの主張は覆されました。(^^; > ALL いやぁ、勉強になるなぁ ---- simple implement is best. T's-Neko

Microsoft, Borland の対応状況(98/08/29)

現在、Visual C++ 5.0, Visual Basic 5.0, Delphi3, C++ Builder3 は、 property に対応しています。

Visual C++ 5.0 の場合
class a {
public:
  __declspec(property(get=get_x, put=put_x)) double x;
  double get_x() const { return elem[0]; }
  void put_x(double d) { elem[0] = d; }
};


This text copyed from Niftyserve