Javaでequalsを実装するとき -失敗するとcontainsが動かない-
hashCodeはもちろん実装しましょう。
で、
public class 俺のクラス {
private String ore;
public 俺のクラス (String ore) {
this.ore = ore;
}
public boolean equals(俺のクラス o) {
return ore.equals(o.getOre());
}
...
}
ってやったらダメ。一見まともに動くんですが…
俺のクラス o1 = new 俺のクラス("hoge");
俺のクラス o2 = new 俺のクラス("hoge");
俺のクラス o3 = new 俺のクラス("foo");
// true
o1.equals(o2);
// false
o1.equals(o3);
Correctionのcontainsがちゃんと動かない。
Set<俺のクラス> os = new HashSet<>();
os.add(o1);//もちろんtrue
os.contains(o1);
//trueになるかとおもいきやfalse
os.contains(o2);
正しいequalsの実装はこう。
public boolean equals(Object o) {
ほげほげ
}
こんなところでしばらくハマってしまった…
ちなみにはまってる間、色々調べていてわかったのですが、
HashSetって内部的には単一のObjectをValueにもってるHashMapなんですね。(java7だと)
Mapのキーの管理にSet使うならわかるんだけど実態は逆で不思議。
org.junit.Assert.assertEquals って
中で比較してんのは equals じゃなくて == なのね…
ただしStringだけはちゃんと比較してる模様。
XP祭り2014に参加してきました! -Agileってなんなんだろう が少しわかったお話- #xpjug
Agile という ものにたいしてここ最近モヤモヤしていたものが、
もやもやの輪郭が少し見えてきた という話をまとめます。
アジャイルを手放して得られたこと から
の続きから。
前の記事で書いたように、アーキテクチャとプロジェクトマネジメントはとても大事で、案件中にホイホイ変えるようなものではない。
とはいえ、考えすぎてもわからないことはある。
で、どーするよ ってところから生まれたのがAgileだというお話。
だからとても画期的だった。
けれど課題があって、
一つが全体整合性の軽視。特にアーキテクチャ。
…うわ、これすげーわかる。
いくらテスト自動化とかしてたって、全体のアーキテクチャを根底から作り直すのはとても時間&リスクがかかることなので、避けられるものなら避けるべき。
実際Scrumだって、Sprint 0で計画するんですよね。
アーキテクチャにかぎらず計画などもそうですが、
「不確実だから途中途中で見なおす機会を作る」のがAgileなんであって、「最初から全部なんてわかりっこないから、ちょっとずつ決めてきゃいいんです」ではない。
…ではないのだけれど、このへんわかってない人間が、
これAgileなんでっ って、Agileという言葉を言い訳に使った状態
これをAgileのダークサイドと鈴木さんはおっしゃっていました。
痛い。
痛い痛い痛い。
なんか思う所ある。心がまじいてぇww
おっさん、心臓ザクザク刺された気がしてマジ辛くなったっすよこの瞬間。
じゃあどうするかって言ったら、
大事なのは「良い物を作りたい」という覚悟!
絶対にやってはいけないのは言い訳。
不確実性と、仲間と、プロダクトと、そういったものと向き合う覚悟が大切なのだとおっしゃってました。
それは…多分大丈夫。
基調講演から
順番前後しますが、関将俊さんによる基調講演についてのレポートです。
関さんのお話は初めてお伺いしたのですが、これまで数年間、
XPをカスタマイズして、自分たちにフィットする「昔XPだったやりかた」をやっていたと思っていたけれど、
XPエクストリーム・プログラミング入門
というXPの教科書を読み直したら、実はXPの枠からは出ていなくて、 守破離のまだ守でした というお話。
これを聞いて、あ、今の俺そういう状況なのかもと。
守破離の「離」です!とか偉そうなことを思ってるつもりはないのだけれど、一方で、
スクラムとか、プラクティス多すぎてこれに全部そうとか無理ですわ 勝手にいいとこだけ頂きますわ
みたいのはずっと思ってたり。
もやもやがほんの少し明確になった経験1。
更に関さんのお話で刺さったのが、
「本当の反復は上から下までやること」というお話。
CIで毎日ビルドしてますとか、ちょっとずつ作っていますとか
と言うのは本当の反復ではなくて、ビジネスサイドも巻き込んで、ソフトウェア開発に必要なすべてのプロセスを「反復」して初めて反復であると。
関さんの現場では現在は、時間の単位として「イテレーション」という概念は残っているものの、あまりタイムボックスにこだわらずに、ストーリーの完了までを実施しているそうです。
【中古】 XPエクストリーム・プログラミング入門 ソフトウェア開発の究極の手法 /ケントベック... |
XP祭り2014に参加してきました! -アーキテクチャとプロジェクトマネジメントが少しわかったお話- #xpjug
3年連続でXP祭りに参加してきました。
記憶が薄れないうちに早速レポートを。
一気に書こうと思ったのですが、いろんな言いたいことが混ざってきたのでXP祭りのレポートらしく、イテレーティブに書いていこうと思います。
アジャイルを手放して得られたこと
今回、大変刺激を受けた発表の一つです。
心にグサグサ来たのは後半の「Agileのダークサイド」のお話なのですが、
前半の、「アーキテクチャとは」「プロジェクトマネジメントとは」が、まず私にドはまり。
まさにこのへんが昨今の自分にとって<なんなのかよくわからない>ポイントだったのです。
ということで、まずはこの点から。
- アーキテクチャとはシステムに関わる利害関係者(ユーザ、開発者、プロダクトオーナー、経営者、…)の要望をうまいことあわせたもの
- これをすり合わせるのがアーキテクトのお仕事
- アーキテクトの仕事は、事前的。開発が始まってからアーキテクチャを変えるのは大変。
- アーキテクトは 利用時品質→外部品質→内部品質→プロセス の順で考える。
- プロジェクトマネジメントの要素は3つだけ。計画/計測/調整
- 計画のズレを計測し、調整(ステークホルダーやメンバーと)するのがプロジェクトマネージャの仕事
- 計画は重要だが、プロジェクトマネージャの腕の見せどころは「調整」。
なのでプロジェクト開始後の、事後的な仕事がメイン。 - プロジェクトマネージャは プロセス→内部品質→外部品質→利用時品質
な る ほ ど。
「いろんな利害が対立する中で、いい落とし所を見つけるのが良い設計」というのは、以前に先輩エンジニアの方からも聞いたことがありました。
◯◯さんはこうしてほしいと思っている
それをやったら☓☓システムに影響が出てしまう
ならばこのやり方ではどうだっ
のような、ある種の「調整作業」が設計の本質なのかもと。
一方プロジェクトマネジメントについて。
ガントチャートやらタスクボードやら、広い意味でのプロジェクトマネジメントにはもろもろ「見える化」の方法がありますが、
その目的は「計測」。
そしてその結果を元に「調整」することがプロジェクトマネジメントであると。
自分が最近抱えていたもやもやの一端が少しくっきりしたような気がした学びでした。
発表後半については次の記事で。
更新系のAPIって難しい1 部分更新のはなし
勉強不足なのは承知のうえで、実務上困ってることとか考えていることを今日は書いてみたいと思います。
例えば以下の様なデータを取り扱うREST APIをつくろうとしたとします。
- あるECサイトにおいて、商品の料金を取り扱うAPI
- 商品は既にDBに登録されていて、料金のみこのAPIで扱う
- 1商品あたり、料金は2種類ある。通常料金と、お得意様用料金。
- 2種類の料金は、以下の組み合わせが許容される
初期投入の時は、こういうデータをPOSTすると思います。(Item IDはURIに入れるほうが正しいかも)
{ "itemID": 123,
"rate":[
{"rateType":"通常料金", "value":"1000"},
{"rateType":"お得意様用料金", "value":"800"}
]
}
通常料金のみも可能なので、こういうリクエストもありです。
{ "itemID": 123,
"rate":[
{"rateType":"通常料金", "value":"1000"}
]
}
お得意様料金のみはNGというルールがあるので、このようなリクエストは許容されず、エラーになります。
{ "itemID": 123,
"rate":[
{"rateType":"お得意様用料金", "value":"800"}
]
}
新規登録はとてもシンプルですね。
一方、困るのが、更新の時のRequest設計です。
全部送ってこいやモデル
APIの実装者として一番簡単なのは、「部分更新は認めない」「全部送ってこい」です。
元のデータがこういう状態で
{ "itemID": 123,
"rate":[
{"rateType":"通常料金", "value":"1000"},
{"rateType":"お得意様用料金", "value":"800"}
]
}
こういうリクエストが来たら…
{ "itemID": 123,
"rate":[
{"rateType":"お得意様用料金", "value":"800"}
]
}
通常料金は、「設定なし」に変更される という考え方です。
ですので、既存のデータがどうであれ、このリクエストはNGです。
なぜならこのリクエストを実行すると、お得意様用料金のみ存在し、通常料金が存在しない という、ルール上許容されていない状態になるからです
このような、全更新モデルですと、APIの実装はシンプルになります。
予想される更新結果が全てリクエストに含まれているので、DB上の既存の値がどうあれ、このリクエストに対してルールのチェックをすればいいからです。
一方で、APIの使い手であるクライアントのアプリケーションは不便を強いられます。
くアライアントの実装者は、
「通常料金だけ更新する画面」のを作りたいかもしれません。
そのような場合、クライアントは一旦Get系のAPIで通常料金/お得意様料金双方を取得した上で、通常料金だけ書き換えて、通常料金とお得意様料金双方をAPI送る必要があります。
部分更新モデル
だったら、rateオブジェクトは送られてきたものだけ更新する
というルールにしたらうまく行きそうな気がします。
こういうリクエストが来たら、
{ "itemID": 123,
"rate":[
{"rateType":"通常料金", "value":"1000"}
]
}
お得意様用料金は何もせずに、通常料金だけとにかく1000に更新する。
これなら、クライアントは使い勝手がいいです。
ではこのようなリクエストが来た場合どうするでしょう?
{ "itemID": 123,
"rate":[
{"rateType":"お得意様用料金", "value":"800"}
]
}
この場合少し複雑で、まず、DB上の現在のデータを調べ、通常料金が存在するかしないかを確認します。
通常料金が存在していれば、問題ありません。
一方、通常料金が存在していない場合(つまり、もともとどっちの料金も入っていなかったケース)、このままではお得意様用料金のみ登録されてしまうので、エラーにしないといけません。
同じような話ですがもう一例。
{ "itemID": 123,
"rate":[
{"rateType":"通常料金", "value":null}
]
}
あまり正しくないかもしれませんが、valueにnullがあった場合、その料金を空にするというルールにしたとします。
このようなリクエストが来た場合、やはり既存のデータによって、このリクエストが成功するか失敗するか決まります。
- 既存のデータが通常料金のみの場合 → 成功。料金が何も登録されていない状態になる
- 既存のデータが通常料金・お得意様用料金双方ある場合 → エラー。通常料金のみ消すのはルール違反なので、更新されない。
つまりいいたいのは
このように、APIで取り扱う対象の中にルールが存在した場合に、
- 全部送ってこいやモデル
→リクエストに対してチェックを行えばいいのでシンプルな実装になる。
反面、クライアントは使いづらい。
(更にクライアントとサーバ間で無駄な通信が発生することになる) - 部分更新モデル
→クライアントの実装はシンプル。
反面、APIの実装は複雑になり、バグの温床にもつながる
(今まで触れていませんでしたが、実際に、この手の複雑さが引き起こしているバグを見つけたことがあります)
という問題がありまして、
私が、更新系のAPIって難しいなぁと思っている理由の一つになっています。
…といいつつ、実は自分の中では、これがベターかな?という
解決方法が見えているので、次回はそれについて触れたいと思います。
※古く、かつ実装面に関しては言及されていませんが、IFがどのようにあるべきかについて議論された記事を見つけました。
http://www.infoq.com/jp/news/2010/11/rest-partial-update
私がかつてTDDに対して誤解していた点
半年前のTDD Boot Campでの気付きだけど、改めて。
※これを読んで、「いや、相変わらず誤解してるんですけどww」 と思われた方は是非ご指摘いただけると幸いです…。
1. 先にテストを書けばTDDなんでしょ?
⇒リファクタこそがTDDの命!
Red -> Green はまだ入り口。そのあといかにRefactorしていくかが大事。
2. 最初に全ケース網羅するテストを書かなきゃなんでしょ?
⇒TDDで書くテストコードは設計のための作業なので、必ずしもそうじゃないと思う。
ただし、それとは別に、「変更に対してコードを守る」ためのテストは整備したいので、
後でもいいから最終的にはケースは網羅してほしい。