著作一覧 |
いや、なに、その、あの、……と続く。
class Foo def initialize() @bar = nil end attr_accessor :bar end foo = Foo.new if foo.bar.nil? #foo.barがnilという状態か判定 puts 'nil' else do_something(foo.bar) endいちいち変数に代入しなければどうか?
class Foo { Bar bar; Bar getBar() { return bar; } } ... foo = new Foo(); if (foo.getBar() == null) { System.out.println("null"); } else { doSomething(foo.getBar()); } ...ゲッタで戻されるのは常にBarのインスタンスであり、そのインスタンスが自分はnull(
== null
というのは、JavaにおけるRubyのobject#nil?
と同義だと考えればreturn null;
を排斥する理由にはならない。return null;
で良い。まあ、配列ならば要素0の配列を使用すべきだとは思うが、それは利便性の問題に過ぎない。あと、1対0..*の関連なら最初からコレクションのインスタンスを生成しておいて常にそれを元にして(イミュータブルにして)返すのが正しい実装だろう。object == null
という記述は、object
に対して「おまえはnullか?」というメッセージの送信なんだから、全然、おかしくないということ。#isBarNull
は単なる便利な(オブジェクトの取得をスキップして直接該当オブジェクトの)状態判定が可能なヘルパメソッドかな。
オブジェクトの関連に着目するならば、
1対0...1の関係ならば、return null;またはreturn インスタンス; というより何も考えずに、return フィールド;
1対{0|1}...*の関係ならば、return コレクションフィールド; ミュータブルかイミュータブルかはメソッドの定義次第。単なるゲッタならばイミュータブル。最小数と最大数が固定なら配列でも可(100万要素なら配列のほうが絶対的に早いから、配列はパフォーマンスオプションとして利用できる)。
1対1 これでnullなのは異常な状態なんだから、アクセスしたやつがNullPointerExceptionで死ぬも良し、IllegalStateExceptionをスローするのでも良し。
取得可能かどうかの判定メソッドというのは、オブジェクト間の関連とは無関係なヘルパメソッド。
もちろん、それがオブジェクト間の関連ではなく、操作しているオブジェクトの状態ならば、その状態の取得メソッドとなるからヘルパメソッドではない。
Bar bar = foo.getBar(); doSomething(bar);ってのは、fooにASKしている。これをTELLに変えればこの問題は起きない。
foo.doSomething(); ... class Foo { Bar bar; void doSomething() { if (bar != null) { bar.doSomething(); } } }なぜ、ASKする?
プラグマティックプログラマーから。
プラグマティックに考えると値オブジェクトの場合はどうよ? とかあるけど、値オブジェクトってのは方便だからそれを基準に考えてもしょうがなかろう。
atomicityまで考えると、状態問い合わせっていうのはまずいですね。その場合は、上のが全部、やばくなる。TELLの例にsynchronizedを付ければOKかな?
えーと、良心の問題として付け加えると、自分で完全に制御できるものについては、こんな感じになるけれど、仕様を切って渡す場合には、手続き的になるのは気にしてないです。また、できたプログラムを後はよろしく、する場合にもask, get, and do it myselfのようなものは書きます。(追記:つまりトランザクションスクリプトパターン)
仕様をプログラムに落とし込むところで、誰もがプラグマティックプログラマーのように考えられるとは想像できないし、仕様というものを箇条書きにしろ、文章で書くにしろ、手続きとして記述せざるを得ない以上、また、細粒度のオブジェクトまでダイアグラムに書くのは無意味(だと思うからTDDマンセーなのだが)だと考えているから、誰もにオブジェクトのインタラクションとして頭に浮かべろというのは、いささか無謀ではないかとも思います。そこで工夫をしながら限られた時間の中で、やってのければそれに越したことは無いというスタンスです。
ちなみに、== null が複数の意味を持つというのは理解できませんでした。C++だとオペレータオーバーロードがあるから複数の意味を持たせられるけど、そうでなければ左辺のオブジェクトにnullかどうか問い合わせるということ以外にどんな意味があるんでしょう?
ゲッタを使って取り出したオブジェクトに、お前はnullかと問い合わせるのがダメで、取り出す前に問い合わせるのがOKってのは、カプセル化を破ってると端的には思います。だって、自分が存在しているかどうかは、そのオブジェクトが知っているべきことですな。そのオブジェクトの持ち主が知っているってのは持ち主がエラ過ぎです。でも、持ち主が知っていても構わない場合もあって、それはその持ち主と持たれているオブジェクトの関連によって決定されます。一意ではありません。でも、== nullという記述は一意です。
(追記:nullが一意ではないという意味かな。nullはan invalid or uncreated objectだから2種類の意味を持つ。それにそもそも型を持たない。だから一意ではないということなのかな。とは言えある型の変数に入れられたら、その型のオブジェクトの参照という意味付けはされているわけだし、invalidかuncreatedかはその時点で問うても意味ないような気がする。)
ちなみに、マジックナンバーは大好きなので、壁に/etc/magicをプリントアウトしたものを貼っていたりします(ここまで書くとネタだと思うかな)。
public class St { public static void main(String[] args) { String s = null; System.out.println(s.valueOf(123)); } }
s
は、Stringオブジェクト(追記:わけわからん。正しくは、「Stringオブジェクトの参照」)なので、StringクラスのvalueOfメソッドは呼べます。ただし、インスタンスは== nullが真なので、インスタンスメソッドは呼べません。
ジェズイットを見習え |
わははははは。<br># artonさんって、一発ぎゃぐも素敵
ASK & GETじゃatomicじゃないでしょ。
おまけのコードはEclipse使ってると警告が出ます。
== nullが複数の意味を持つというのは、人間が頭の中で解釈するときに複数の解釈方法があるという意図で使っています。例えば hoge == null をみたプログラマは「hogeが取得できる状態にない」「hogeがIllegalな状態だ」「hogeを持っていない」「hogeでない」など、どういう意味で理解するか揺れがあるということです。
そんな解釈を許容させるということは、モデリングが失敗しているのではありませんか? その例だと、順に状態遷移があるオブジェクト、エラーでも平気なオブジェクト、hogeと1対0..1の関連のオブジェクト、単なるバグの4つの異なるhogeの持ち方をするクラスです。したがって、呼び出し側にはそれが異なるということを意識させなければなりません。したがって、単にgetHogeというメソッド名ですべてをくくろうとするのがまずいのだと思います。<br> ただ、事前に確認処理を行わせ、かつあくまでもゲッタ主体でインターフェイスを規定するのならこのうち、1番目については、isReadyForHoge()は当然あるべき、2番目はisValidHoge()があったほうが良い(というより本当にそんなオブジェクトはありか?という疑問も。例外のほうが良さそう)、3番目はnullで良し(hasHoge()を実装してももちろん良い)、4番目は問題外ということでしょう。ただし、カプセル化の破壊を言い出すと、クライアントがステートを常に意識しなければならないというのも厳密には妙だとは思います。(じゃあ、どうしろと言うのかと訊かれてもとりあえずはわかりません)<br>これは、nullが複数の意味を持つのではなく、getHoge()というメソッドが複数の意味を持っているのです。なぜなら、複数の状態を返送してしまうのだから。インターフェイスを憎んでnullを憎まず。<br>#Eclipseを使ってたんですね、なんで便利だと言うか疑問だったけど一応、氷解しました。<br>#なかなか勉強になりました。おもしろい着眼点を教えてくださってどうもありがとうございました。
TDDが好きなのも当然、こうやってイテレーションするスタイルなんだからしょうがないね、推敲大好き。<br>モデリングが失敗ではなく、APIの決定に失敗と書くべきですね。1番目は状態があるんだから、getHogeではなくdoHogeで処理結果を返す。返るのがobjectなら、nullは処理の失敗。あるいは、例外でNotReadyExceptionなど(値オブジェクトでも良いかも。getResult()とgetHoge()を持つ値オブジェクトを返すとか)ーただし、本来はTellにするべき。2番目は例外。3番目はまさにnullを返すべき場合。4番目は問題外(getHogeでhogeでないとは?)。ということではないでしょうか。
インターフェースと実装という切り口ならば...と思いましたが、もう少し煮詰めてみます。<br><br>getHoge()に機能をつめすぎてるというのはその通りだと思います。nullを返すまえにホントはartonさんのようにちゃんと考えなくてはいけないのに、nullを返してしまうことで何も考えずに逃げられてしまうことが問題なのだと思います。<br><br>null禁止はその逃げ道をふさぐことになるので、より安定して、確実なシステムに近づけるのにある程度の効果は期待できるはずです。<br><br>#Eclipseのページはそろそろ消そうかな?と思っていたのでお役に立てて幸いです。<br>#こちらこそ貴重な意見をありがとうございました。