> > 委譲の話はこの際直接関係ないですよね。
>
> おおありです。責務をもちすぎでクラスを
> 切り出すだし、委譲することがあります。
いや、ここではアクセサを作ることで実装を変更できる、という話をしていたはずで、
実装変更のとき、「Pointに切り出してgetterの処理を委譲する」ということは
確かにあるかもしれないけれど、
委譲の使い道はそれだけじゃないし、アクセサにより実装変更できてよかったね、
というケースは別に「実装を委譲に変更する」場合だけじゃないわけで、
この両者は直交していると思うんですが。
> > getter/setterを機械的に
> > 付けたからって「カプセル化」になるわけじゃない、というのが、
> > 私が言いたかったことです。
>
> 結局、私がこれをちゃんと読みこんでいなかったから
> 誤解を生じたみたいですね。ごめんなさい。
誰も彼もが誤解するようなら、書いた側に責任があるかと思います。
このページも、掲示板での皆さんのご意見により、いくらか修正が入っています。
ご意見がいただけるのはありがたいことです。いや本当に。
> むしろ私が気になるのは、オブジェクト指向について説明しようとする人が、
> 非オブジェクト指向なプログラマの能力(というか認識というか)を
> 過剰に軽視していると思われることが多い、ということです。
補足です。
とはいえプログラマのスキルは千差万別で、Leptonさんのページにあるように
http://www.amy.hi-ho.ne.jp/~lepton/program/p2/prog246.html
ローカル変数の意味さえわからないような人も世の中に入るようです。
だから本やWebページなどで何かを説明しようとする場合、どうやっても
ある程度読者層は限定されるんですよね。
「疑りぶかい〜」に関して言えば、構造体とポインタがわからない人には
わけわからないと思います。まあでもそれはしょうがないんじゃないかと。
> > 関数に「○○してください。」とお願いする、という感覚は、
> > Cプログラマであっても持っているはずです。
>
> > printf("hello, world.\n"); は、printfという関数に、
> > 「"hello, world.(改行)"と表示してね」とお願いしているのです。
>
> 私や私のまわりの人々はもってないですね。
私や私のまわりの人々はもっていますね。
…ってこれじゃあ水掛け論だなあ。
> > 実装詳細は普通のプログラマは知りません。
>
> この文章は、ここの議論とは関係ないですよ?
そう思いますか?
誰かに何かを「お願い」する、という場合、「細かいことは任せた」という
ニュアンスが入っていると思います。
| 各人が自分の責務をまっとうしつつ、知っている人にお仕事の
| 依頼をしあうことは、実世界でもよくある話で
という話をするのなら、これはとりわけ重要でしょう。
いや、もしタケさんが、
「私や私のまわりの人々は『お願い』という言葉にそんなニュアンスは
持たせていない」というのであれば、それは、私とタケさんとで
「お願い」という言葉の認識がズレているということになります。
だとすれば、タケさんが、オブジェクト指向の初学者に向かって
「オブジェクト指向ではオブジェクトにメッセージを送って仕事をお願いする」
と説明したところで、説明された側の人は「お願いする」という言葉から
何を想像したかわかったもんじゃない、ということですから、
結局、そんな曖昧な言葉をもとにプログラミングパラダイムの説明をするのは
危険だ、ということになりますね。やっぱりメタファは所詮メタファである、と。
> それから、あなたの文章、根拠がないのに
> 「はず」というのが多すぎますね。
具体的にはどこのことでしょうか。
今回の文脈からすると
> > 関数に「○○してください。」とお願いする、という感覚は、
> > Cプログラマであっても持っているはずです。
これですかね。
私もCのプロジェクトの経験が結構ありますが、
「画面表示はこのモジュールを使う」とか、
「データベースの管理はこのモジュールに任せればいい」とか、
そんな言葉は日常的に飛び交っていますよ。
実際、ある程度の規模のプログラムになると、そういった考え方がなければ
そもそも開発できないでしょう。
むしろ私が気になるのは、オブジェクト指向について説明しようとする人が、
非オブジェクト指向なプログラマの能力(というか認識というか)を
過剰に軽視していると思われることが多い、ということです。
何度もネタにしている「憂鬱なプログラマのためのオブジェクト指向開発講座」
という本では、最後に実例として「魔方陣を解くプログラム」を作っているのですが、
その「C言語での例」はこんなのになってます。
http://member.nifty.ne.jp/maebashi/programmer/object/list1.txt
Cプログラマは再帰も使えない「はず」だってことですかね。
先週は、慣れない仕事で死んでました。返事が遅くなりましてすみません。
> だれが目くじら立ててるんですか?
え? 「絶対おかしい」ってタケさん自身が書いていると思うんですが。
> わたしの(および、わたしの周辺の人々)の
> 場合は、getXXは、字面の意味でなく
> 「○○ください。」のイメージで使ってますね。
> 暗黙知しているのでいいかなという考え方です。
getXXごときに「イメージ」も何もないのでは、と思いますが…
getXXの中身が結局 return XX; であることを知っているからこそ
「暗黙知」が通用するわけで、「メタファは所詮メタファ」だと
私は思います。
私はC人間ですが、
私的には関数呼出しと同じ意味だと思っています。
ジュース自販機.お金投入()
ジュース自販機.ボタン押下()
ジュース自販機.商品取出し()
タバコ自販機.お金投入()
タバコ自販機.ボタン押下()
タバコ自販機.商品取出し()
所在が異なるだけという感じで。
他では
カーネル(だっけ?).FileCopy()
GDI.DrawText()
というのも、実際には所在なんて気にしないでFileCopyやDrawTextを使いますし。
この形を”メッセージ”だよ。”関数呼出しとは違うよ”と言われたら、混乱します。
例が悪いかも知れませんけど。
>> > 責務を分担するために委譲を用いることはよくあります。
>>
>> 委譲の話はこの際直接関係ないですよね。
>
>おおありです。責務をもちすぎでクラスを
>切り出すだし、委譲することがあります。
>この場合、単純な座標値を属性としてもって
>いたがPointクラスとして切り出したほうがよい
>として切り出したとしても呼び出し側では
>始点のX座標値などはほしいわけですから
>切り出したクラスに仕事を委譲しています。
横から失礼します。
えーと、いま委譲という言葉は
「オブジェクトの再利用における継承と委譲」の委譲として
扱われているのでしょうか?
それとも組織論などにおける「直轄と委譲」の委譲と
して扱われているのでしょうか?
> タケさん、(ぱ)さん
>オブジェクト指向の入門書を読むと、
>「オブジェクト指向では、オブジェクト同士が相互に
>メッセージを送ることにより協調動作しながら動作する」などと書かれています。
>こんな説明を読むと、まるで各オブジェクトにそれぞれ
>プロセスが割り当てられていて、プロセス間通信で
>動作するかのように思えますが、
少なくとも私の周辺では思いません。オブジェクト指向が
わかってくるとそういうイメージするとみんな言います。
ただ、たとえば「協調動作」という言葉を、機械よりコンピュータ
よりにとらえるとそう思われても仕方のない表現かなと
も思います。
#協調作業とかのほうがいいのかもしれないです。
>「使用者に公開する必要のない変数や関数を隠すこと」
>をカプセル化と呼ぶのなら、これだって立派なカプセル化です。
> つまり、カプセル化は、別にオブジェクト指向に固有の概念ではありません。
そのとおりですが、「カプセル化」というコンセプトを
用語として定義していることが重要です。
それまでには、あなたのいうようなことを端的に
あらわす言葉はなかったはずです。
批判の対象にならないです。
スローガン、コンセプト、メタファの重要性
については野中氏の知識創造企業に記されているので
読んでみてはいかがですか?
あなたの文章、むだにいやみったらしく書いていたり、
決め付けている部分が多くてもったいないです。
端的にこう書かれている文献が多いが、かくしかじか
の理由よりおかしいし、わたしはこれこれこういう理由で
こう思うと書いたほうが、オブジェクト指向へのパラダイム
シフトができてない人に共感を得られやすいと思います。
あなたが、書いてある文章の事実だけ直視すれば、そのとおりだと
思われるところが多いで、もったいないと思います。
> > 各人が自分の責務をまっとうしつつ、知っている人にお仕事の
> > 依頼をしあうことは、実世界でもよくある話で
> > その雰囲気でコードを組めることがオブジェクト指向
> > のよい点のひとつです。
>
> これはCで「モジュール」を意識したプログラミングをしていても同じことですね。
> static版のboard.cでも、board.cに「石を置く」という処理を依頼しているわけです。
> で、オブジェクト指向ではこれがどう変わるかといえば、
> 私にとっては、まず最初に伝えるべきことは、「インスタンスが複数存在しうる」
> ということだろう、と思うわけです。
私はオブジェクト同士がネットワークを構成して
仕事を依頼しあうというのが一番最初だと思っています。
> > 責務を分担するために委譲を用いることはよくあります。
>
> 委譲の話はこの際直接関係ないですよね。
おおありです。責務をもちすぎでクラスを
切り出すだし、委譲することがあります。
この場合、単純な座標値を属性としてもって
いたがPointクラスとして切り出したほうがよい
として切り出したとしても呼び出し側では
始点のX座標値などはほしいわけですから
切り出したクラスに仕事を委譲しています。
> getter/setterを機械的に
> 付けたからって「カプセル化」になるわけじゃない、というのが、
> 私が言いたかったことです。
結局、私がこれをちゃんと読みこんでいなかったから
誤解を生じたみたいですね。ごめんなさい。
> > オブジェクト指向の概念では、
> > 自分の知っているオブジェクトに
> > 「○○してください。」とメッセージを送る
> > ことにより仕事を成し遂げます。動き方として実態
> > が似たようなものでも、関数とは概念が違います。
>
> 関数に「○○してください。」とお願いする、という感覚は、
> Cプログラマであっても持っているはずです。
> printf("hello, world.\n"); は、printfという関数に、
> 「"hello, world.(改行)"と表示してね」とお願いしているのです。
私や私のまわりの人々はもってないですね。
オブジェクト指向が普通の感覚になったあと
Cのような言語を使ったときは、そういうイメージかも
しれないなとは思いますが。
オブジェクト指向の場合は、自分の知っている
オブジェクトに対してお願いをするイメージが
があるので「メッセージ」というコンセプトが
非常に大事であると思っています。
識者の講習を聴講した際、
オブジェクト指向に慣れてくると、
部下とか受付に仕事を頼んだときに
「ああ、自分はメッセージを送っているなあ。」と
感じた、というエピソードがありました。
私も同じ気持ちになったことがあります。
そういう自然に仕事をこなしているときの
同じ感覚で上流から下流まで開発できるのが
オブジェクト指向のよい点だと思っています。
> 実装詳細は普通のプログラマは知りません。
この文章は、ここの議論とは関係ないですよ?
それから、あなたの文章、根拠がないのに
「はず」というのが多すぎますね。
> > ちょっとばたばたしているので
>
> 私も、今日はいろいろあってかなり疲れてますので、簡単に。
>
> > あります。getterのネーミングは絶対おかしいと思っています。
> > outputXXとか、giveXXtoMeとかのほうがいいと思っています。
> > #後者は冗談です。
>
> このへんの、メソッドのネーミングに混乱があるということについては、
> 私も(混乱の元になるから)よくないと思っていますが、
> だからといって、getXXX()というメソッドを書いている人に向かって、
> 「こんな名前を付けてるようではオブジェクト指向の理解は一生深まりません。」
> なんて言おうとは思いません。
わたしの(および、わたしの周辺の人々)の
場合は、getXXは、字面の意味でなく
「○○ください。」のイメージで使ってますね。
暗黙知しているのでいいかなという考え方です。
> 「getXしてね」というメッセージを送るなんてのは変だからそれはおかしい、
> と目くじら立てたって、「ハァ?」としか思えない人が多いはずです。
> それより、動きがわかった上で、例を見たり自分でプログラムを組んだりして、
> いつか「壁を越える」方が、ずっと理解が早いと思いますよ。私は。
だれが目くじら立ててるんですか?
> ところで、今、蔵書管理プログラムを作成しているのですが、
> ここで構造体からQソートし、CSVファイル形式で書き込む
> 部分で苦戦しております。何か良いアルゴリズムは無いものでしょうか?
そこまで決めているのなら、アルゴリズムも何もないような気もしますが(^^;
注意することとしては、
標準ライブラリのqsortは配列相手にしか使えない(だから連結リスト等を
使っているのなら自分で書く必要がある)、とか、
CSVファイルを出力するときに、書名などにコンマやダブルクオートが
入ってると面倒くさい、とかでしょうか。
時間がないのでちょっとだけですが、
アクセサに関して言えば、Eiffelみたいに、
・メソッドに引数がないときには呼び出しに()を付けなくてもよい。
・フィールドは外部から見えるけど、左辺値を持たない。
という形が実用上は便利な気がします。
アクセサを書かなくても「p.x」という参照ができて、
ただしp.xへの代入は最初から禁止されていて、
いざ実装が変わったなどでアクセサを書きたくなったら書けばよく、
呼び出し側を変更する必要はない、と。
わきみちですが、Objective-CのフレームワークであるCocoaでは
getterにはget〜が付かず、名詞から始まります。「お茶」と言ったらお茶
が出てくるような感覚でしょうか。
getを付けると意味が違ってきてしまい、呼び出し側で用意した引数の
中に値を取り出してくるメッセージ(メソッド)になります。
メンバ変数への直接アクセスとメッセージ呼び出しは表記法が全く異なる
上に、メンバ変数にアクセスするようなコードはまず書くことが無いため
そうなってるんでしょうけど。
> 恥ずかしながら。
>
> > > display.showScore(current_point);
> > この例では、showScoreするのはdisplayですから、
> > displayに「showScoreしてね」というメッセージを
> > 送っている、という解釈が成立するのかもしれませんが、
>
> > point.getX()の場合、pointに「getXしてね」というメッセージを送る、
> > という解釈は通用しません。Xをgetするのは呼び出し側だからです。
> > こういうこと、考えたことないですか?
>
> 私は こういうこと、一度も考えたことなかったです...ダメじゃん→私(^^;)
>
> どんな関数でも、あくまで主語は「私」だと思い込んでました。
> display.showScore(); // (私が) display に Scoreを show する
> point.getX(); // (私が) point から X を get する。
> っていうもんだと思ってたので、一切、矛盾があるなんて、考えなかったです。
> そうか、オブジェクト指向では、オブジェクトが主語になるのかぁ。
>
> > CとC++の違いが理解できました。C++はCの拡張版って聞いたんですけど、
> > C++でCのプログラムが実行できない事もあるのですね。
>
> 困ったことに厳密な下方互換性は ないんですよ。
> (ほとんどの場合、キャストするくらいでうまくいくんですけどね)
>
> 他には関数の宣言に()に何も書かなかった場合の処理、ポインタ変数の宣言、
> struct{}の中身の埋め込まれる順番とか、sizeof('A')の結果などが、
> 互換性がないですね。
>
> # 私はC++に詳しくないので、
> # もっと詳しい人が解説してくださるのを期待しましょう...f(^^;)
>
ご無沙汰してます、職柄上休日だけは、PCと無縁の場所にいたいので
返信が遅れましたm( )m。Cの歴史は、色々な本に載っていて社内でも
よく見ておりますが、
Sizeof('A')で、CとC++でサイズが違うというのは意外でした。
UNIXマシーンを勉強する時、気をつけないといけない部分ですよね。
他にも詳しい情報が得られるなら、知識だけでも個人的に知っておきたいです。
ところで、今、蔵書管理プログラムを作成しているのですが、
ここで構造体からQソートし、CSVファイル形式で書き込む
部分で苦戦しております。何か良いアルゴリズムは無いものでしょうか?
> CとC++の違いが理解できました。C++はCの拡張版って聞いたんですけど、
> C++でCのプログラムが実行できない事もあるのですね。
困ったことに厳密な下方互換性は ないんですよ。
(ほとんどの場合、キャストするくらいでうまくいくんですけどね)
他には関数の宣言に()に何も書かなかった場合の処理、ポインタ変数の宣言、
struct{}の中身の埋め込まれる順番とか、sizeof('A')の結果などが、
互換性がないですね。
# 私はC++に詳しくないので、
# もっと詳しい人が解説してくださるのを期待しましょう...f(^^;)
恥ずかしながら。
> > display.showScore(current_point);
> この例では、showScoreするのはdisplayですから、
> displayに「showScoreしてね」というメッセージを
> 送っている、という解釈が成立するのかもしれませんが、
> point.getX()の場合、pointに「getXしてね」というメッセージを送る、
> という解釈は通用しません。Xをgetするのは呼び出し側だからです。
> こういうこと、考えたことないですか?
私は こういうこと、一度も考えたことなかったです...ダメじゃん→私(^^;)
どんな関数でも、あくまで主語は「私」だと思い込んでました。
display.showScore(); // (私が) display に Scoreを show する
point.getX(); // (私が) point から X を get する。
っていうもんだと思ってたので、一切、矛盾があるなんて、考えなかったです。
そうか、オブジェクト指向では、オブジェクトが主語になるのかぁ。
> ちょっとばたばたしているので
私も、今日はいろいろあってかなり疲れてますので、簡単に。
> あります。getterのネーミングは絶対おかしいと思っています。
> outputXXとか、giveXXtoMeとかのほうがいいと思っています。
> #後者は冗談です。
このへんの、メソッドのネーミングに混乱があるということについては、
私も(混乱の元になるから)よくないと思っていますが、
だからといって、getXXX()というメソッドを書いている人に向かって、
「こんな名前を付けてるようではオブジェクト指向の理解は一生深まりません。」
なんて言おうとは思いません。
名前に統一性がないのは確かに問題だけど、いくらgetXXX()と書いてたって
だいじょうぶ少なくともそれがどんな機能を果たすかはみんなわかってます。
だからこそ実際にシステムが構築できているわけですよね。
「getXしてね」というメッセージを送るなんてのは変だからそれはおかしい、
と目くじら立てたって、「ハァ?」としか思えない人が多いはずです。
それより、動きがわかった上で、例を見たり自分でプログラムを組んだりして、
いつか「壁を越える」方が、ずっと理解が早いと思いますよ。私は。
ちょっとばたばたしているので
面白いところだけ返事します。ごめんなさい。
> > display.showScore(current_point);
> ...
> > 関数呼び出しといってしまうと、
> > 上記をdisplayがshowScoreするというふうに
> > 左から右に読んでしまって、
>
> これはむしろ「オブジェクト指向」のクラスライブラリなどが混乱している
> 事例として挙げられるんではないでしょうか。
> メソッドはたいてい動詞になるわけですが、その主語が誰であるか、
> というのは、実は混乱しているものです。この例では、showScoreするのは
> displayですから、displayに「showScoreしてね」というメッセージを
> 送っている、という解釈が成立するのかもしれませんが、
> point.getX()の場合、pointに「getXしてね」というメッセージを送る、
> という解釈は通用しません。Xをgetするのは呼び出し側だからです。
>
> こういうこと、考えたことないですか?
あります。getterのネーミングは絶対おかしいと思っています。
outputXXとか、giveXXtoMeとかのほうがいいと思っています。
#後者は冗談です。
getterに限らず、操作に関して呼び出し側が主語になる
ようなネーミングをしている事例は分析レベルでも
よくあり、混乱の元となっていると考えます。
waiter.order(food);みたいなモデルを
書く人が多いです。
waiter.acceptOrder(food);みたいに
かける人は少ないと思います。
#後者のorderは名詞
#この事例は動物オブジェクトに歩けと
#メッセージを送る説明と同様、全然よくないですが、
#ちょっと思いつかなかったので、勘弁してください。
ちょっと話がそれますが、
#部分的には関係します。
車のドアを開けると車内ランプがつくというような
組込み系のプログラムをオブジェクト指向で
分析、設計、実装を行ったときがありました。
最下層では、
ドアのセンサーがONになるとメモリのある番地の
あるビットがたつという実装になっており、
それを無限ループで捕捉します。
//呼び出し側はセンサーオブジェクト
while(true){
if(m == 0x11){
door.open();//openメソッドの内部でランプをつける
...
}
}
となりますが、開くのはアクターである
人で、変だと思い、openedにしました。
しかし、openedしてね、あるいは、
openedされろとメッセージを送るというのは、
理解不能で、困りました。
結局、openedメソッドを実装する人の
立場になれば、受動態のほうがよいということで
納得しました。
あとで気づいたんですが、
同様のことは、JavaGUIのイベント委譲モデル
の実装側でも起こっていたとおもいます。
イベントハンドラ側を実装する一般開発者は
困らないのですが。
#StateパターンのContextクラスでも
#似たようなことが起こると思います。
イベントもメッセージ送信で実装しないと
いけないこともJavaやC++のまずいところかなと
思っています。モデルではステートチャートに
イベントの概念があるので問題は起こらないですが。
#もちろん、状態遷移モデルを意識しなかったら
#同じことになってしまいますが。
まとめ:
・操作の主体があいまいになりがちで、今の自分の
中では、オブジェクト指向モデリング、プログラミングの
最大のなぞとなっている。
・お仕事の依頼もイベントもメソッドで実装するのは気持ち悪い。
#C#だとうまくいくんだったと思います。
手抜き文章ですみません。他の部分については、
今度、書きたいと思います。
#いつになるかわかりませんが。
CとC++の違いが理解できました。C++はCの拡張版って聞いたんですけど、
C++でCのプログラムが実行できない事もあるのですね。
> Point getStartPoint();
>
> というメソッドをつけてしまうでしょう。これじゃタケさんの言うような
> 実装の変更にも耐えられないし、
なんか妙なことを書いてしまったようなので訂正します。
getStartPoint()を先に書いたとしても、インタフェースを変えずに実装を
> class Line
> private int x1_;
に切り替えることは可能ですよね(効率悪いけど)。失礼しました。
> カプセル化も壊れている(setStartPoint()を
補足ですが、
これはgetStartPoint()が「return start_point_;」になっていて、
かつPointがimmutableでない場合、
と解釈してください。
> オブジェクト指向の概念では、
> 自分の知っているオブジェクトに
> 「○○してください。」とメッセージを送る
> ことにより仕事を成し遂げます。動き方として実態
> が似たようなものでも、関数とは概念が違います。
関数に「○○してください。」とお願いする、という感覚は、
Cプログラマであっても持っているはずです。
printf("hello, world.\n"); は、printfという関数に、
「"hello, world.(改行)"と表示してね」とお願いしているのです。
実装詳細は普通のプログラマは知りません。
> display.showScore(current_point);
...
> 関数呼び出しといってしまうと、
> 上記をdisplayがshowScoreするというふうに
> 左から右に読んでしまって、
これはむしろ「オブジェクト指向」のクラスライブラリなどが混乱している
事例として挙げられるんではないでしょうか。
メソッドはたいてい動詞になるわけですが、その主語が誰であるか、
というのは、実は混乱しているものです。この例では、showScoreするのは
displayですから、displayに「showScoreしてね」というメッセージを
送っている、という解釈が成立するのかもしれませんが、
point.getX()の場合、pointに「getXしてね」というメッセージを送る、
という解釈は通用しません。Xをgetするのは呼び出し側だからです。
こういうこと、考えたことないですか?
> 各人が自分の責務をまっとうしつつ、知っている人にお仕事の
> 依頼をしあうことは、実世界でもよくある話で
> その雰囲気でコードを組めることがオブジェクト指向
> のよい点のひとつです。
これはCで「モジュール」を意識したプログラミングをしていても同じことですね。
static版のboard.cでも、board.cに「石を置く」という処理を依頼しているわけです。
で、オブジェクト指向ではこれがどう変わるかといえば、
私にとっては、まず最初に伝えるべきことは、「インスタンスが複数存在しうる」
ということだろう、と思うわけです。
> インターフェースが変わらないことが重要
> なのです。
もちろんそうです。インタフェースを変えずに実装を変えることができるのは、
アクセサを書くことの利点のひとつです。
で、Pointごときにそんなのが必要なのか、ということを、
メリットとデメリットを秤にかけてちゃんと考えるべきだ、と私は言っているのです。
また、単純に「フィールドにはgetter, setterを付けるべきだ」と教えられた人は、
class Line {
private Point start_point_;
という実装があったとき、
Point getStartPoint();
というメソッドをつけてしまうでしょう。これじゃタケさんの言うような
実装の変更にも耐えられないし、カプセル化も壊れている(setStartPoint()を
呼ばずともstart_point_を変更できる)わけで、
「機械的にgetter/setterを付けろ」と教えるのは危険です。
> 責務を分担するために委譲を用いることはよくあります。
委譲の話はこの際直接関係ないですよね。この場合、getX1()を実現するために
Pointのメソッドを呼んでいるようですが、矢印の向きを逆にしてみると…
class Line {
private Point start_point_;
...
}
↓
class Line
private int x1_;
...
}
既に書いたように、最初の実装に対して機械的にアクセサを付けたら全てが崩れます。
そういうことをひっくるめ、ちゃんと考えるべきで、getter/setterを機械的に
付けたからって「カプセル化」になるわけじゃない、というのが、
私が言いたかったことです。
>実のところ一種の関数呼び出しです。
>こんなものに「メッセージ」などという
>大仰な名前を付けていることが、結果としてオブジェクト指向
>の理解を妨げていると思います。
まったく違います。むしろ逆です。
オブジェクト指向の概念では、
自分の知っているオブジェクトに
「○○してください。」とメッセージを送る
ことにより仕事を成し遂げます。動き方として実態
が似たようなものでも、関数とは概念が違います。
何かのゲームのようなアプリケーションを組むとして、
画面に現在の点数を出したいとします。
以下のようなコードがあるとします。
display.showScore(current_point);
おそらく、呼び出し側は、コントローラ的なオブジェクト
になると思います。
呼び出し側がdisplayにshowScoreしてねと
メッセージを送るのです。
つまり、右から左に読むイメージです。
関数呼び出しといってしまうと、
上記をdisplayがshowScoreするというふうに
左から右に読んでしまって、
オブジェクト指向の理解は一生深まりません。
各人が自分の責務をまっとうしつつ、知っている人にお仕事の
依頼をしあうことは、実世界でもよくある話で
その雰囲気でコードを組めることがオブジェクト指向
のよい点のひとつです。
>データフィールドは常にprivateにして、
>それに対してpublicなgetXXX(), setXXX()というメソッドを作る」
>なんてことを機械的にやってたら、 結局データフィールドを
>publicにしているのと同じことです。
これもぜんぜん違います。
class Line
private int x1_;
...
public int getX1(){
return x1_;
}
}
↓
class Line {
private Point start_point_;
...
public int getX1(){
return start_point_.getX();
}
}
インターフェースが変わらないことが重要
なのです。責務を分担するために
委譲を用いることはよくあります。
パフォーマンス的に問題ないなら、
機械的にやってかまいません。テストでも
必要になりますし。ただし、あるオブジェクト
に大して外部から値を設定してはまずい場合
もありますので、setterについては、削除するかどうかの
検討を最後にはしないといけませんが。
以上です。「責務の割り当て」、「委譲」を勉強したほうがいいと
思います。
> はじめまして、
はじめまして。
> Java謎と"イキバタ"の本を読んで前橋氏のファンになったものです。
ありがとうございます。大変励みになります。
> 疑問とは、以下の部分に付いてです。
> http://member.nifty.ne.jp/maebashi/programmer/object/naze.html
> > しかし、getX(), getY(), setX(), setY() を作って
> > それぞれをsynchronizedにしたところで、
> > この仕様ではマルチスレッドには対応できません。
>
> 私が勉強した限りではxとyを触る全てメソッドをsynchronizedメソッドに
> すれば、同期が取れないこと等は起きないはずなんですが...
実はこの件についてはこの掲示板で既に話題になっているのですが、
検索機能があるわけじゃなし、探せないですよね。抜粋します。
301のnekopさんのレスより:
↓↓ここから↓↓ここから↓↓ここから↓↓ここから↓↓
void setXY(int x, int y) {
point.setX(x);
// ひと休み。
point.setY(y);
}
これをスレッド A が setXY(10, 20) 、スレッド B が setXY(20, 30) と
同時に呼び出すと、 x=20, y=20 なんて結果になったりします。
ひと休みの部分でロックが開放されるからです。
↑↑ここまで↑↑ここまで↑↑ここまで↑↑ここまで↑↑
304の私のレスより;
↓↓ここから↓↓ここから↓↓ここから↓↓ここから↓↓
> これをスレッド A が setXY(10, 20) 、スレッド B が setXY(20, 30) と
> 同時に呼び出すと、 x=20, y=20 なんて結果になったりします。
これが、「setのXとYが分かれていたりしたのでは」起きてしまう変な現象ですね。
もうひとつの、「getとsetが分かれていたり」したのでは起きてしまう
現象は、たとえばPointのX座標を1インクリメントしようとして、
x = p.getX();
p.setX(x+1);
と書いたとき、ふたつのスレッドが以下のように実行すると
x = p.getX(); ←スレッドA
x = p.getX(); ←スレッドB
p.setX(x+1); ←スレッドA
p.setX(x+1); ←スレッドB
xは2回インクリメントされたにもかかわらず、1しか増えない、というものです。
で、「Javaの格言」で、setterを書かないとマルチスレッドのときに困るでしょ、
と挙げられている例が、まさにこの「xのインクリメント」なんですよね…
↑↑ここまで↑↑ここまで↑↑ここまで↑↑ここまで↑↑
Pointを移動させるとき、その途中の状態を他人に見せたくないのなら、
move(x, y)というメソッドを作らなければなりませんし、
xやyに加算や減算を行いたいのなら、translate(dx, dy)というメソッドを付けて
引数として移動量を与えてやらなければなりません。
# 実際、現在のjava.awt.Pointクラスはそうなってますが、synchronizedは
# 付いてないようですね。
はじめまして、
Java謎と"イキバタ"の本を読んで前橋氏のファンになったものです。
"疑り深い"記事を読んで疑問に思った部分があります。
"Java質問スレ"で聞こうか迷いましたが、、、こちらで質問させていただきます。
疑問とは、以下の部分に付いてです。
http://member.nifty.ne.jp/maebashi/programmer/object/naze.html
> しかし、getX(), getY(), setX(), setY() を作って
> それぞれをsynchronizedにしたところで、
> この仕様ではマルチスレッドには対応できません。
私が勉強した限りではxとyを触る全てメソッドをsynchronizedメソッドに
すれば、同期が取れないこと等は起きないはずなんですが...
あるスレッドがsychronizedメソッドを抜けるまでそのオブジェクトの
全synchronizedメソッドがアクセスできないことについてですか?
是非、変なことの具体例が是非知りたいです。Webプログラマーを
目指している自分にとっては気になって夜も眠れません。
(新しい本や記事で書いてくれると嬉しいです)
参考にしたサイト
Locking an Object
http://java.sun.com/docs/books/tutorial/essential/threads/monitors.html
Other threads cannot call a synchronized method on the same object until the object is unlocked.
> ポインタというのは一応、データ等をメモリに保存しておくためのアドレス
> なんですよね。これが16進数で4ビット感覚で開いているのは分かるんですが
「4ビット感覚」、というのが「4バイト間隔」のミスであると仮定すると、
「ポインタ完全制覇」の最初のサンプルで、int型の変数に&を付けて
アドレスを表示させているものがありますが、このアドレスが4byte間隔だと
いうことでしょうか。
これは、対象としている変数がint型で、現在のたいていのCPUでは
intが4byteだからこういう結果になるのであって、たとえばchar型の
変数を使えば1byte間隔になる可能性はあります。
たとえば
char a[10];
として、
&a[0], &a[1], &a[2], &a[3], &a[4], ...
を順に表示すれば、アドレスは1byte間隔になっているはずです。
> ポインタ=アドレスなのか?
そう考えて問題ないと思いますが、ただし、普通「ポインタ」という時には
「ポインタ型変数」を指すこともあるから注意が必要…ということを
「完全制覇」のp.32に書きました。
> あとList2-7のソースがVC++6.0ではコンパイルエラーになりました。
これは、本多さんも指摘されているように、CとC++では仕様が異なるためです。
> > これが16進数で4ビット感覚で開いているのは分かるんですが
> 4bit...っていうのは4byteの書き間違いでしょうか?だと考えて以下を書きます。
あ、これって16進数の1桁が4 bitって意味だったのかな。
> これが16進数で4ビット感覚で開いているのは分かるんですが
この表現が私にはわかりませんが...^^;
16進数か10進数かは表現の問題ですよね。
16進数で0x10は10進数の16と全く同じ値なのはご存知ですよね?
16進数表記でaddressを記述することが非常に多いのですが、これは
機械を直接いじっている人間にとっては10進数表記より
16進数表記の方がわかりやすいというところから来ているわけで
10進数で表記しても構わないものなんです。
4bit...っていうのは4byteの書き間違いでしょうか?だと考えて以下を書きます。
addressを4 byte=32 bitで表現することが多いのは、
現在 普及しているcomputerが32 bitでaddress表現するためで
4byteで表現するかどうかは本質ではないんですよ。
とりあえず、pointer演算をしなければpointer = addressと考えていいと思います。
> 『ポインタをたぐりよせる』という表現は
> 『メモリーのアドレスを参照するor呼び出す』と同じような事なのでしょうか?
同じと考えていいと思います。
> あとList2-7のソースがVC++6.0ではコンパイルエラーになりました。
> 8:int_p = malloc(sizeof(int));
> C:\zousho\inputBook.cpp(8) : error C2440: '=' : 'void *' から 'int *' に変換することはできません。(新しい動作 ; ヘルプを参照)
C++はCと異なる仕様があってvoid*をint*に直接は格納できないんです。
int_p = (int *)malloc(sizeof(int));
と、明示的にキャストしてあげればいいと思います。
返信感謝です。
>よろしければ、どのあたりが難しいと思われるか教えていただけませんか?
>同じところで悩んでいる人が他にもいるかもしれませんし。
ポインタというのは一応、データ等をメモリに保存しておくためのアドレス
なんですよね。これが16進数で4ビット感覚で開いているのは分かるんですが
ポインタ=アドレスなのか?読んでいるうちにポインタとは何なのかが
つかみ難くなってきます。『ポインタをたぐりよせる』という表現は
『メモリーのアドレスを参照するor呼び出す』と同じような事なのでしょうか?
メモリとポインタの関係は容易につかめましたが、アドレスとポインタの違い
みたいなものが、なんかまだモヤモヤしてよく分からないです。
あとList2-7のソースがVC++6.0ではコンパイルエラーになりました。
8:int_p = malloc(sizeof(int));
C:\zousho\inputBook.cpp(8) : error C2440: '=' : 'void *' から 'int *' に変換することはできません。(新しい動作 ; ヘルプを参照)