だったら突っ込みはもっと少なかったのではないかなぁ(´Д`)
「関数呼び出し」などという大仰な名前にこだわっているのは
そこらへんに根っこがありそうですけど。
setter/getter意味ねーのか全部publicでいいや、と思わせがち
なとことか、言葉足らずの部分もわかっててやってるにしちゃ
罪深すぎます。
すみません、改行なしで投稿してしまいました。
前回の投稿は削除して頂けると有難いです。
ーーここからーー
ひとつの反証を上げます。
AppleScriptというMacOS上で動作するスクリプト言語があります。
言語仕様は不完全ながらもオブジェクト指向で、ある意味完璧なカ
プセル化を実現しています。
例えば、
「set name of document 1 of application "TextEdit" to "no name"」
は、「TextEdit」という名前のApplicationの1番目のDocumentの名
前を「no name」に変更します。
実装としては、applicationインスタンスに所属するdocumentイン
スタンスをインデックス参照で呼び出して、その属性値であるname
をdocumentクラスに実装されたset命令(command)で変更しているの
ですが。少なくとも、上記のAppleScriptスクリプト上には関数呼
び出しは登場しません。
このset命令はメッセージであって、関数呼び出しではありません。
Applicationクラス(或はDocumentクラス)はObjective-CやJAVAで記
述されたクラスです。AppleScriptは、その実装方法に関係なく、
規定された属性と命令だけを利用してプログラムを記述します。受
け付けた命令(command)をどう処理するかは実装次第で、AppleScri
ptはそれに関わりません。
前橋さんは、JAVAとC++の実装を例にとって、帰納的にオブジェク
ト指向を説明しようとしていますね。
しかし、「オブジェクト指向プログラミング言語」とは「オブジェ
クト指向プログラミング」をサポートするための機能を仕様として
持った、単なる言語実装に過ぎません。特定の言語でメッセージ機
構を関数呼び出しで実装していたとしても、オブジェクト指向プロ
グラミングに於けるメッセージと関数呼び出しとを同一視すること
はできないのです。
> > C言語のfopen()とは何か違うんですか?
> 同じでしょ。
> > fopen()もfileをaccessするためのオブジェクト(の様に見えるもの)を
> > バコバコ作っているように見えますが、
> 私にもそう見えます。
と、すると、
「オブジェクト指向での再利用性」と 関数(ライブラリ)による再利用性との違いって
なんでしょう?っていうのはこれから読み続けるとわかるのかなぁ?
> シングルインヘリタンスながら、継承とメソッドオーバーライドまで
> 実現しています。
用語に対する私の理解が少し混乱してます。
インヘリタンスって継承と同じ意味じゃないんでしたっけ?
上の文章で言うと、インヘリタンス=継承+メソッドオーバライト
って感じに使ってますか?
となると多重継承がうまくできるのがオブジェクト指向言語の特徴なのかしら?
#多重継承ってあまりよくないことだと聞いたことがあったような。
#理由は忘れたけど。っていうか私の勘違いかも。f(^^;)
Cで多重継承は...難しいだろうなぁ。どうやるんだろう?
ついでに質問です。
objective-Cとかで できる(?)って言ってる「動的束縛」ってどんな機能でしょう?
実行時に関数(の様なもの)を(関数名以外の何かの手段で)選択する機能かな?
XtNameToWidget()+ 関数() とか、dlopen() + dlsym()みたいな動作をするんですか?
前橋さんって事情が全部わかっていて、わざとボケたフリしてんのかな?
だったらいわゆる“ネタにマジレス”になりそうですけど(笑)。
>「人間、誰しも低レベルの概念のほうが理解が早いものです」。
そして、これの最大の落とし穴は
低レベルの説明で理解したつもりになって、それ以上に思考を進めようとしない
弊害に陥りやすい点です。
>「理念」の説明を延々とされることが、結局理解の妨げになっていると思いませんか?
これに関しては別の掲示板で書いたけど、説明をえんえんと聞かすよりも
とっとと演習させたほうが早いだろうと思います。人間の理解というのは
どうしても自分が経験した範囲や教育の範囲に縛られるので、その範囲を
はずれたことはわかったつもりになっていて、そうでもない、と…。
前橋さんの“メッセージ送出=単なる関数呼び出し”説が危険なのは
前橋さんが知っている実装の帰納的な類推で言っているだけであって
今後出てくるであろう/あるいは前橋さんが知らない実装で反証される
可能性がいくらでも出てくるであろうという点です。
はじめまして。YOUsukeともうします。
「疑りぶかいあなたのためのオブジェクト指向再入門」について質問いたします。
「メッセージは関数と同じ」なんてのが方々で話題になってるようですが、この「関数」というのは
一体どんなモノを想定しているのでしょうか。
雰囲気としてはある命令群から別の命令群へのジャンプを想定しているようですが。
しかしそれはあくまでジャンプであって関数の本質ではありません。
プログラムにおいて「関数」がなぜ「関数」と呼ばれるようになったかと考えると、それが数学上の
関数と機能が酷似していたからだと推測されます。
そして現在プログラミングにおいて使われる「関数」は数学上の関数の定義とはかけ離れたがものが
多いです。
日記にも書きましたがプログラムの「関数」が「関数」と呼ばれる理由。「メッセージ」が「メッセ
ージ」と呼ばれる理由は実装方法・実行方法に依存するものではないと思います。言語・開発手法の
背後にあるスタンス・考え方という上位の概念に依存しているものでしょう。スタンス・考え方が反
映された結果が呼び名になっているはずです。
オブジェクト指向言語の理解を、言語機能とその背景にある考え方・開発手法との結びつきを構築す
る作業、とするならば結びつきを表し・説明するための用語もその考え方に沿ったものでなければ深
い理解は得られないと思います。
これが「メッセージは関数と同じだ」という記述に対する僕なりの答えです。
「疑りぶかいあなたのためのオブジェクト指向再入門」に対する感想を述べます。
うまくまとまらないため箇条書きのようになってしまう事をお許し下さい。
どのような学習者を想定しているのかが曖昧です。前提にある知識や目標を考えた方がいいです。
既存の説明方法の欠点を挙げる事は学習者に対してあまり利益が無いと思います。トラウマと劣等感
を軽減する程度でしょう。不毛です。無意味に脇道にそれる事は学習者に対する不利益でもあります。
オブジェクト指向の考え方まで解説したいのかオブジェクト指向言語の機能だけを解説したいのか不
明確です。オブジェクト指向の考え方を抜きにした場合はオブジェクト指向言語の機能の大半は「な
ぜこんなことをする必要があるのか」と思わざるを得ないものとなります。この疑問が解決されない
限りオブジェクト指向に対する苦手意識を取り除く事は不可能だと考えます。
例を挙げます
http://member.nifty.ne.jp/maebashi/programmer/object/response.html
の後半に「board.put(x, y);」という記法に関するやりとりがあります。「「board」というイン
スタンスにputという処理を依頼するものです。」という意見に対して「気持ちの問題」という回答
をなされています。言語機能にだけ注目した場合はそうかもしれません。しかし背景があるのです。
オブジェクト指向においてはインスタンスに処理を依頼するという考え方(気持ち)で動作を分析し
、その考え方でコードを作成することが望まれます。そしてその考え方に則ってコードを作成した結
果が上記のコードであり、また上記のコードを実現する機能がオブジェクト指向をサポートしている
と言えます。それを「気持ちの問題」とするのはオブジェクト指向の考え方を無視しているか言語の
機能を軽んじているかのどちらかです。どちらにしてもそれを推賞することが学習者のオブジェクト
指向の習熟を進める結果にはならないと思います。
メソッド呼び出しの記法で混乱し挫折する程度の人は多分そのままの思考様式ではオブジェクト指向
を習熟するのは困難でしょう。これは説明方法には依らないと思います。というか、そういう人がど
うやってC言語を習得したのか謎ですが・・・。
確実に目標達成可能なオブジェクト指向プログラミングの学習カリキュラムの作成と言うのは興味深
いです。 僕だったらこんな風に作るでしょう。
・可能な限り多数の「挫折した人」に大してインタビューをする
・一般的なオブジェクト指向言語の機能を詳細に調査しその背後にある考え方を分析する
・逆にオブジェクト指向の考え方からそれをサポートする言語機能を分析する
・学習者のモデルを複数作る
・学習者のモデルをもとに複数の明確な最終到達目標とその判定基準を作る
・学習者のモデルと目標に合わせたカリキュラムを作成する
また経験的にプログラミングの習得には実践が重要であると思います。そしてオブジェクト指向プロ
グラミングの習得にはメソッド単位の作成では不十分であるていどまとまったプログラムをいくつも
作っていくことが重要だと思います。その際に作成したプログラムを評価したり問題解決をサポート
する人がいると学習がスムーズになるでしょう。実践は本人の習熟度・環境に合わせたものが望まし
いのでサポートする人が学習者を観察して課題を与えるのがベストです。その際のマニュアルも作成
するのがベストでしょう。
多分これくらいしないとまともなものは作れないような気がします。
しかしこんなことをしていてはあっというまに1年2年がかかってしまいますね。
学習において重要なのは「問題の発見と解法の発見の追体験」だと思います。僕が作るとしたらこれ
を重視したいですね。
今後の「疑りぶかいあなたのためのオブジェクト指向再入門」に期待しています。
頑張って下さい。
乱文失礼いたしました
追記:
今後もまとまらない文章、長い文章、手もとにおいておきたい文章は日記に書くと思います。
必要があれば適宜引っ張ってきます。
http://www.fastwave.gr.jp/diarysrv/trinity/200309b.html#20030918-1
http://www.fastwave.gr.jp/diarysrv/trinity/200309b.html#20030918-2
(引用はご自由に)
ちゅーちゅーねずみ さん wrote:
> 知人のMac屋さんに聞いてみたらObjective-Cは生成されたオブジェクトコー
> ドを逆アセンブルしてもobjc_msgsndとかになっちゃってるそうで。関数呼び
> 出しとかじゃぁねぇよ!といわれました(笑。
僕は、これでも関数呼びだしと言っちゃっていいと思います。
インタプリタ言語の関数呼びだしなんて、普通、これよりもっと複雑な
手続きを経て呼ばれますが、それでも関数と呼びますよ。
ただし、
(ぱ)さん wrote:
> 「メッセージ送出」と呼ばれているのは、 実のところ単なる関数呼び出しです。
この文章には、実は抵抗があります。
というのは、並列オブジェクト指向言語の中には、ABCL のように、メッセー
ジ送出が、異なる実行スレッドへの通信メッセージ送出である言語もあるから
です。もし、この文章が
: ほとんどのオブジェクト指向言語では、「メッセージ送出」と呼ばれている
: のは、呼ばれる関数の検索方法がちょっと特殊なだけの、単なる関数呼び出し
: です。
だったら、特に違和感がないのですが。
それから、setXXX(), getXXX() のくだりについては、僕もじゃばっくさんや、
sadakazuw さんと同様、常に setter/getter を使った方が良いと思います。
デバッグの時や、開発期間後半に仕様変更や設計洩れがあった場合に、
フックするだけで済むのは大きなメリットですから、setter/getterは、
むしろ常に機械的に用意した方が良いでしょう。
setter/getter を使わない方が良いのは、性能プロファイルをとった結果、
効率上仕方がないと判断した時だけだと思います。もし 言語が C++ だった
場合には、inline 化されるので、ほとんどそういう心配もない筈です。
以下は、文意には賛成なんですが、文章スタイルについてのコメントです。
> 世間の入門書では、オブジェクト指向といえば「カプセル化」「継承」
> 「ポリモルフィズム」ということになっていると思いますが、
このあたりの文章は、ちょっと過激だと思います。:-)
別に、良く読めば変なことは書いてないんですが、ぱっと見、無駄に
誤解を招きかねないというか。
「カプセル化」「継承」「ポリモルフィズム」が、オブジェクト指向『言語』
としての必要条件だというのは確かだと思うし、たぶん(ぱ)さんも、その点
について異論はないんじゃないですか?
このあたりの文章は、これらについての否定的な表現を抑えて、これらも
あるけど、それだけじゃないんだってことだけ強調した方が、万人に受け
入れられやすいと思います。
あと、「マルチプルインスタンス」というのは、「マルチプルインヘリタンス」
と字面が非常に似ているので、一言「インヘリタンスじゃなくてインスタンスだよ」
って断っておいた方が良いような。
最初読み間違えて、あれれ?っと思いました。
> 実は「Javaの格言」を見ていないので分からないのですが、getter/setterの必要性を
> そのスレッドにおける不可分の話だけで説明されているのでしょうか?
すみません、現在「Javaの格言」が手の届くところにないので、
「スレッドにおける不可分の話だけ」だったかどうかが確認できません。
具体的な「困る例」としては、それしか書いてなかったと思いますが。
> getter/setterを用意していれば、最初はフィールドの値をreturnしたり代入したり
> しているだけの造りでも、後から他のオブジェクトの値を参照したり設定したり
> するような改造したりするのも容易になりますよね。デバッグ用にsetterの
> パラメータをロギングするようにしてみるとかも。
とまあそのように、具体的なメリット/デメリットを考慮した上で
getter/setterを付けるというならわかります。
「機械的に」やってはいけないでしょ、と言っているわけです。
> 後からどれくらい必要になるかは怪しいですが、取り合えずgetter/setterを
> 用意するのはそんなに悪くはないんじゃないかと思うのです。
> (いちいち用意するのは面倒なんですけどね…)
あとはデメリットとのトレードオフなんですよね。
「面倒」というのも立派なデメリットだと思いますし、
利用者側のソースが汚くなりますし。
Windows 98 Second Edition 上の Borland C++ 5.2 ですが、
再現できません。
> あなたのローレル指数は142.479137です。
> 判定:痩せ過ぎです
という結果になります。
> > 掲示板に貼られた箇所以前の
> > バグのせいで変なことが起こった可能性は否定できないので、
> > まだそちらで再現できるのであれば、ソース全体を、手を加えずに
> > そのまま、掲示板に貼っていただけませんか?
すみません、見ての通り書き込みがたくさんありましたので反応が遅れました。
貼っていただいたソースをうちの環境で試すと、やっぱり、
あなたの身長を入力してください>170
あんたの体重を入力してください>70
あなたのローレル指数は142.479137です。
判定:痩せ過ぎです
となります。不等号が逆なせいで判定は変ですが、ちゃんと動いている
ように見えます。
コンパイラはbccで試しましたが、うちのOSはWindowsXPです。
どなたかWin98を使っておられる方で、再現させられる方はいますか?
> C言語のfopen()とは何か違うんですか?
同じでしょ。
> fopen()もfileをaccessするためのオブジェクト(の様に見えるもの)を
> バコバコ作っているように見えますが、
私にもそう見えます。
> あれは、マルチプルインスタンスとは表現しないんですか?
表現してよいのではないでしょうか。
FILE *fp = fopen("hoge", "r");
fprintf(fp, "hello.\n");
が、
FILE *fp = new FILE("hoge", "r");
fp->printf("hello.\n");
になったからって、「見た目」以外の何かが変わったとは、私には思えないです。
> XmCreateXXXX()は?
これはもうまさにCで(無理やり)オブジェクト指向を実現した例ですよね。
シングルインヘリタンスながら、継承とメソッドオーバーライドまで
実現しています。
継承やメソッドオーバーライドまで実現しようとすると、
Cでは言語の助けがないからいろいろ変なことをしなければなりませんが、
カプセル化とマルチプルインスタンスまでなら、構造体を不完全型で
隠せばよいわけで、かなり容易に実現できます。
> このところのやりとりで、SmalltalkやObjective-Cな人が、
> C++やJavaと、SmalltalkやObjective-Cとの間に線を引きたがる根拠が私には
> わからないのです。
たぶんこのあたりはSmalltalk、Objective-C、Rubyにその他(ついでにLispなんかもいいかもしれない)といった物をメインにやってないと分からないのかなぁ?と思います。
> 「メッセージ送出なんてただの関数呼び出しのことじゃねえか」という私の
> 主張に対し、
この主張自体がC++とかJavaしかやってない人でないと出てこないわけです。
> いやあ、それは単にメソッド名と引数が文字列であることに騙されている
> だけで、これも、「文字列で相手を探す関数呼び出し」と見て「も」
> 構わないと思いますよ。私は。
「も」であるならあの喜んで〜〜の部分は削除すべきでしょう。
というかタイトルと、前書きからさも「汎用オブジェクト指向再入門」といった感じでありながら、
実態はCプログラマによるCプログラマのためのC++/Javaに於けるオブジェクト指向再入門であるという
のがいけないんじゃないでしょうか。
でないとGoogleで飛んできた入門者なりに裏付けなしに暴言を吐かせてしまいかねないですよ。
って、感じなんですけどねぇ。
すみません、なんだか途中で送信してしまったようです。
管理者権限で非表示にすることはできるのですが、面倒なのでそのままにします。
[314]の書き込みの「もうひとつの面から。」以降は無視してください。
で、ここから続き。
もうひとつの面から。
「理念」…というかどうかはともかくとして、
「機能」の面から言って、メソッドはやっぱり関数呼び出しでしょう。
少なくともCプログラマの目から見れば。
もちろん動的束縛は入りますが、それは、「このようにして実際に呼び出す
関数を選んでいる」ということを、後からでも学べばよいことです。
これは実装の話ではありません。たとえObjective-Cのメソッド呼び出しを、
「関数の検索」ではなく「switch case」で実現する処理系があったとしても
(スーパークラスで再検索する覚悟があれば不可能じゃない気がする)、
元ソースがメソッドの格好をしている以上、「関数呼び出し」という説明で
問題があるとは思えません。たとえばswitch caseになっていたとしても、
そんなのはそれこそ実装詳細です。
# もちろん動的束縛の話はどこかのタイミングで知らなければならないですが。
> “理念”面でいえば
> メッセージは「オブジェクトの外側の人がオブジェクトに対してお願いするもの」
> メソッドは「オブジェクトの内側の人がお願いをかなえるためにすること」
> という違いかな。これもツッコミどころはあると思うけど、しいてたとえるなら
> こんな感じ。静的なOOP言語に得意な人はこのたとえで言えば“お願い”を
> ピックアップして考えるから、そんなもの一緒じゃねえかと思うんでしょうね。
こういうたとえ話が理解を妨げて…という話はさておき、
Cプログラマだって、関数やモジュールに「お願い」する感覚はあると思うし、
Javaプログラマなら、ひとつの「お願い」に対して「かなえるためにすること」が
たくさんあるのは普通の話だと思いますよ。
このところのやりとりで、SmalltalkやObjective-Cな人が、
C++やJavaと、SmalltalkやObjective-Cとの間に線を引きたがる根拠が私には
わからないのです。
「メッセージ送出なんてただの関数呼び出しのことじゃねえか」という私の
主張に対し、
「いや、メソッドの動的な選択が入るから「ただの」関数呼び出しってのは
乱暴ではないか」
という反論ならわかるんです。
# だからって動的束縛を最初に覚えなければならないとは思いませんが。
メソッドが動的に選択されるのはC++でもJavaでも同じこと。
もちろん、選択の方法は違いますし、結果としてできることにも違いはありますが、
だから「メッセージ」になるんですか?
ところで、Javaでリフレクションを使うと、
Id dog = new Dog();
dog.sendMessage("bark");
と書くことで、Dogクラスのbark()メソッドを呼び出す、ということが可能です。
…これなら「メッセージ送出」に見える?
いやあ、それは単にメソッド名と引数が文字列であることに騙されている
だけで、これも、「文字列で相手を探す関数呼び出し」と見て「も」
構わないと思いますよ。私は。
う、ちょっと間が空いたら、いろいろ書き込みが増えてる…
まあ、ゆっくりいきます。
> という点では同意してると思うんですよね。
はい。
> その上で、#includeのネストを禁止されると困る「頻度」がどの程度か、
> というのが問題なのだと思います。
頻度っていうと難しいと思います。
むしろ、ある種の設計パターンだと、禁止されると大弱りだし、
別の設計パターンだと、禁止されても構わないってことかと。
> > これに対し、DRW_Line.h 中の struct DRW_Line の定義が、
> > struct DRW_Line {
> > struct DRW_Point *start_point, *end_point;
> > };
>
> この場合は、DRW_Pointを不完全型で宣言するわけですよね。
いえ、そうとは限りません。
勿論 DRW_Point を不完全型としても良いわけですが、そうする
必要はありません。
DRW_Line.h が、struct DRW_Point へのポインタのみを参照している
のであれば、たとえ struct DRW_Point の定義が DRW_Point.h で
公開されている場合でも (もちろん公開されてない == 不完全型で
ある場合でも)
#include <DRW_Point.h>
とせず、
struct DRW_Point;
だけで済ますことができますよね。
> また、列挙や#defineは、不完全型で隠すことはできませんし。
その列挙や #define を利用するインターフェースが、一つのヘッダ
ファイル内で閉じていれば、ネストは必要ないですよね。
問題は、複数のヘッダで共通に利用している列挙や #define がある
場合だと思います。Indian Hill では、こういう場合、独立した
DRW_Params.h のようなヘッダを作ることを求めているんだと思います。
> > や、あるいは
> > struct DRW_Line;
> > だけだった場合、
>
> このケースでは、DRW_Lineが不完全型宣言されているわけですが、
> ではstruct DRW_Lineの宣言の本体はどこにあるかというと、
> kitさんの前提では、DRW_Line.cでしょうか。
この場合はそう考えていました。
> struct DRW_Lineはそれでよいかもしれませんが、たとえば、
> 「ウインドウ」を抽象化するDRW_Windowではどうでしょうか。
:
> …とここまで書いて、想定している「モジュール」の粒度が
> 違うんじゃないかという気がしてきました。kitさん的には、
> DRW_Window構造体も、DrawLib内部とはいえ「丸見え」にすべきでは
> なくて、DRW_Window.c内に封入すべきなんでしょうか。
そう思います。
Window ぐらいに複雑な型になると、最近の僕は迷わず不完全型に
してしまい、DRW_Window.c 内に封入してしまいます。
あとで性能プロファイルをとった結果、問題が見つかったら直す
かもしれませんが… でも、たいてい性能ネックは別のところに
あるので、そういうことは殆どありません。
> でも、そうだとしても、DRW_DrawLine.cではXlibのWindowが
> 必要になりそうですから、DRW_WindowにgetXWindow()を付けたとしても
> その戻り値はXlibのWindowになるはずですし…
Xlib は、ここで前提としている設計ポリシーとは違うポリシーで
設計されていますから、それは仕方がありませんね。
DRW_Window_X11.h のような内部向け共通ヘッダでは
#include <X11/Xlib.h> をしておく (ヘッダのネストをここでは許す)
のが良いかもしれません。ただ、僕はこの場合 Indian Hill 式に、
各ソースファイルで #include "DRW_Window_X11.h" の前に
#include <X11/Xlib.h> と書くことを要求しても、それほど抵抗を
感じません。たぶん、各 .c ファイルで、結局 Xlib の関数を呼び
出すことになるせいかな?
ふたつの点から反論します。
私は結構至る所にこれを書いてるんですけれども、
「人間、誰しも低レベルの概念のほうが理解が早いものです」。
> C言語が苦手だけどアセンブラが得意な人が、C言語の自動変数
> という考えがよくわからないから機械語に落としてみて、
> 「なんだ。やつらは変数のスコープが1つの関数内でのみ有効とか
> なんとか難しいことぬかしてやがるが、ただのスタック変数じゃん!」
ちょうどよい例なのでこの例で話しますと、自動変数の話をするんなら、
スタックの話からしたほうがよいでしょう。実際、「ポインタ完全制覇」では、
2章にて、自動変数のアドレスをprintf()で表示したりして、それがスタック上に
あることを示しました。まあ人によって感性に違いはあるでしょうが、
再帰呼び出しなんて、スタックを意識しなければ理解するのが難しいと思います。
> 注意しないといけないのは、
> 自動変数をスタック変数にするというのは、そのように実装するのが楽だから
> というだけであってC言語の規格上は自動変数はスタック変数にせねばならない
> という制約なんかないんですよ。なんなら関数突入時にmallocで領域を確保して
> 関数脱出時に解放することにして、その領域を自動変数にしたってかまわない。
その通りです。だから「ポインタ完全制覇」では、2章の終わりに
「言語仕様と実装について---ごめんなさい、ここまでの内容はかなりウソです」
という節があり、そこでまさに「関数突入時にmallocで領域を確保して
関数脱出時に解放することにして、その領域を自動変数にしたってかまわない」
という説明をしています。
> だから、自動変数はスタック変数だというのは自動変数の“理念”の
> 説明になってない。
ことオブジェクト指向の説明においては、そういう「理念」の説明を延々と
されることが、結局理解の妨げになっていると思いませんか?
「メッセージ」はその最たるものであると私は思っています。
もうひとつの面から。
「理念」…は
> “理念”面でいえば
> メッセージは「オブジェクトの外側の人がオブジェクトに対してお願いするもの」
> メソッドは「オブジェクトの内側の人がお願いをかなえるためにすること」
> という違いかな。これもツッコミどころはあると思うけど、しいてたとえるなら
> こんな感じ。静的なOOP言語に得意な人はこのたとえで言えば“お願い”を
> ピックアップして考えるから、そんなもの一緒じゃねえかと思うんでしょうね。
>
setXを設けることによりxの値をチェックする事もできますし、
クラスの内部構造を変えるような事があった場合、
アクセッサをおくことにより、コードの書き換えが最小限で済みます。
ただ、Pointクラスのような基本的なクラスの場合、
setXの利用によりパフォーマンスが落ちるかもしれないので、
ケースバイケースだと思います。
setXの必要性が無いというのは嘘ではないのですか?
えーっと、JavaとかObjective-Cとかsmalltalkとか知らないのですが、
オブジェクト指向の本質ははマルチプルインスタンスだっておっしゃってますが、
マルチプルインスタンスって、オブジェクトをバンバン作るって言うことでしょう?
#ん?何か誤解してます?
つまりC言語ではできなくてオブジェクト指向的言語Objective-C/SmallTalk/Javaで
できる部分がそこってことなんですか?
C言語のfopen()とは何か違うんですか?
fopen()もfileをaccessするためのオブジェクト(の様に見えるもの)を
バコバコ作っているように見えますが、
あれは、マルチプルインスタンスとは表現しないんですか?
XmCreateXXXX()は?
#おぉ、われながら初心者的なバカっぽい質問だ。少し恥ずかしいな。*^_^*
以前、(ぱ)さんが「よた話」の「ポインタ」の中で
「あくまでポインタを実現するための「実装手段」として
たまたまアドレスを使用しているということに過ぎず、
ポインタという概念自体は、もっと抽象的なものである筈です」
と、おっしゃっているのと同じようにmessageを送るというのは
「あくまでmessageを送ることを実現するための「実装手段」として
たまたま関数呼び出しを使用しているということに過ぎず、
messageを送るという概念自体は、もっと抽象的なもの」
じゃないでしょうか?
とはいえ、「messageを送る」なんていう概念から入るより、
「つまり簡単に言えばC言語の関数呼び出しだよ」という説明はわかりやすいので、
オブジェクト指向につまずいた人を対象にした説明としては妥当かもしれません。
説明としては正確とは言えないまでも間違っているわけじゃないし、
Cはとりあえずできるけど、オブジェクト指向的な説明についていけなかった人が
対象という意味では適切なのかもしれませんが。
つまり私にはわかりやすい(^^)
うーん。HAPPY Macintosh Developing TIME! の掲示板でも書いたんだけど
「メッセージ送出=単なる関数呼び出し」に対する違和感というのは、
それは「メッセージセンディングをどう“実装”するか」の説明にすぎない
という点です。C言語が苦手だけどアセンブラが得意な人が、C言語の自動変数
という考えがよくわからないから機械語に落としてみて、
「なんだ。やつらは変数のスコープが1つの関数内でのみ有効とか
なんとか難しいことぬかしてやがるが、ただのスタック変数じゃん!」
って毒づいているのと同じなんですよ。注意しないといけないのは、
自動変数をスタック変数にするというのは、そのように実装するのが楽だから
というだけであってC言語の規格上は自動変数はスタック変数にせねばならない
という制約なんかないんですよ。なんなら関数突入時にmallocで領域を確保して
関数脱出時に解放することにして、その領域を自動変数にしたってかまわない。
だから、自動変数はスタック変数だというのは自動変数の“理念”の説明になってない。
自動変数の“実装”の話です。理念と実装の次元をごちゃまぜにしている。
“理念”面でいえば
メッセージは「オブジェクトの外側の人がオブジェクトに対してお願いするもの」
メソッドは「オブジェクトの内側の人がお願いをかなえるためにすること」
という違いかな。これもツッコミどころはあると思うけど、しいてたとえるなら
こんな感じ。静的なOOP言語に得意な人はこのたとえで言えば“お願い”を
ピックアップして考えるから、そんなもの一緒じゃねえかと思うんでしょうね。
> これが、「setのXとYが分かれていたりしたのでは」起きてしまう変な現象ですね。
(ちょきん)
> xは2回インクリメントされたにもかかわらず、1しか増えない、というものです。
一度のメソッド呼び出しにおける競合の話と勘違いしていました。
(アトミックの話かと…intだから問題ないのか)
実は「Javaの格言」を見ていないので分からないのですが、getter/setterの必要性を
そのスレッドにおける不可分の話だけで説明されているのでしょうか?
getter/setterを用意していれば、最初はフィールドの値をreturnしたり代入したり
しているだけの造りでも、後から他のオブジェクトの値を参照したり設定したり
するような改造したりするのも容易になりますよね。デバッグ用にsetterのパラメータを
ロギングするようにしてみるとかも。
また、実際にはフィールドに値を設定しないsetterにする事もできるわけで、
> 「publicなsetXXX()メソッドを書いた時点で、そのデータは外部から自由に
> 書き換え可能になっているのですから。」
と言うのはちょっと違う気がします。
(そんな事をするのはデバッグ用途以外にないかも知れませんが)
後からどれくらい必要になるかは怪しいですが、取り合えずgetter/setterを
用意するのはそんなに悪くはないんじゃないかと思うのです。
(いちいち用意するのは面倒なんですけどね…)
早速更新されてる♪と読ませていただきました。
>まあこの文言には煽りも入っているわけですが、 「「メッセージ」なんて言葉を嬉しがって使」うような>連中って、 言語がC++だろうとJavaだろうと、継承もポリモフィズムもなかろうと、 「メッセージ」と
>うい言葉を嬉しがって使っているように見えますよ。 私には。
この文を読むとCをやっててC++に移行してJavaもやってOOPを覚えました。な感じがぷんぷんとしちゃう訳ですが(と、ここで煽るつもりはありませんので。為念)。
「SmalltalkだのObjective-CだのはこれからOOPやろうとしてるとか、やりなおそうって君らには
関係ないだろ?C++とJavaやるにはこんな訳の分からん言語の概念なんざ持ってきても百害あって一
利なしだ。というわけでC++やJavaを使ってるのにメッセージなんて言葉を嬉しがるのはタダのバカ」
という感じにやっていただければ何の問題もないと思います。
知人のMac屋さんに聞いてみたらObjective-Cは生成されたオブジェクトコードを逆アセンブルしても
objc_msgsndとかになっちゃってるそうで。関数呼び出しとかじゃぁねぇよ!といわれました(笑。
匿名さんのメールにも例示(rubyやObjecitive-Cの言語実装とPowerPlantというフレームワークの話)がありましたが、これに対する解答もC++のフレームワークの話とCの話に落としちゃってたので、ちょっと残念かな?と。
レイヤーが違うところのお話はちょっと(^^;
suminさんのサイトのSwikiを見てみてはは〜〜なるほどとおもったのですが、一口にオブジェクト指向
といっても切り口や、体現している概念は全然違っちゃってる場合があるので、一つの言語(と、それに類する言語)による知識で全てを言っちゃえ!というのはかなり乱暴な感じです(キャッチーではありますけど(笑)。
#とはいえ、煽りつきで楽しく読ませていただいております。掲示板で好き勝手な事書きやがってとお思いかと思いますが、これからもがんばってくださいまし。
dentomさん、ちゅーちゅーねずみさん、sumim さん、
とりあえず思うところは
http://member.nifty.ne.jp/maebashi/programmer/object/response.html
こっちに書きました。
私の認識が間違っているようでしたら、また突っ込み願います。
Smalltalkerの端くれとして同じ意見です。
Smalltalk、Self、Objective-C のようにメッセージングメタファを言語レベルで
サポートしている OOPL を想定したオブジェクト指向は除く…というような但し書き
を入れてください。
私は幸いにしてOOPの入り口がSmalltalkだったので(笑。
Objective-Cの人と同様に『「メッセージ」なんて言葉を嬉しがって使ってる奴はたいてい「知ったか」野郎である。』には違和感がありますね。
2ch風に言えばC++厨の戯れ言がまたはぢまったよ(;´Д`) という感じです(笑)。
C++等のStaticなOOLならばというただし書きでも付けといていただけるとありがたいです。
> > > 対応できません。ちょっと考えてみればわかるように、 getとsetが分かれていたり、
> > > setのXとYが分かれていたりしていたのでは、複数のスレッドで実行されたら結局
> > > 変なことが起きます。
> これをスレッド 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のインクリメント」なんですよね…
> 安藤幸央さんのページで、Java 良本・Javaベストブックの投票
> というものをやっています。
http://www.gimlay.org/~andoh/cgi-bin/vote/tvote.cgi?event=javabookvote&show=all
これですね。情報ありがとうございます。
> 「Java謎+落とし穴 徹底解明」も、2票入ってますね。
おー。
これはこれで素直に嬉しいですが(結構古い本なのに…)
1位が「Javaの格言」かあ… あの本、「あれえ?」な記述も
結構あったと思うんですが。
さっそくご意見ありがとうございます。
> (java のことは
> 知りませんが少なくとも C++ では) board がポインタ
> なら、board.put(x,y) じゃなくて board->put(x,y)
> なのでは?
そうでした。
Javaではboard.put(x, y)と書くのですが(boardはもちろんポインタです)、
この文章では、すぐ上で「さて、JavaやC++などのオブジェクト指向言語では」
と書いていますから、 board->put(x, y)と書かなければまずいですね。
boardがポインタであることは、この文脈では結構重要ですし。
修正しておきます。
> > ところで「Javaの格言」という本では、 Pointのx, yをprivateにするメリットとして、
> > マルチスレッド時の排他制御を挙げています。しかし、getX(), getY(), setX(), setY()
> > を作ってそれぞれをsynchronizedにしたところで、この仕様ではマルチスレッドには
> > 対応できません。ちょっと考えてみればわかるように、 getとsetが分かれていたり、
> > setのXとYが分かれていたりしていたのでは、複数のスレッドで実行されたら結局
> > 変なことが起きます。
>
> 複数のスレッドが同じPointオブジェクトを参照している場合、
> ロックはメソッド単位ではなくインスタンス単位に存在するので、
> getとsetが分かれていても問題無いと思うのですが。
void setXY(int x, int y) {
point.setX(x);
// ひと休み。
point.setY(y);
}
これをスレッド A が setXY(10, 20) 、スレッド B が setXY(20, 30) と
同時に呼び出すと、 x=20, y=20 なんて結果になったりします。
ひと休みの部分でロックが開放されるからです。
Vector や Hashtable の同期も一緒です。
こういった手続きは
syncronized (point) {
point.setX(x);
// ひと休み。
point.setY(y);
}
とする必要があります。