前置き
普段はJavaを書いているので、Javaを想定して書きます。
DDDっぽい趣向が入ってしまうのをなんとか避けつつ...(願望)
略称を使わず、総称を使う
例に関して、id
ですら identifier
と書いてしまう筆者なので少し過剰気味かもしれませんが、短く意味が伝わらない名前より、長くて意味が伝わる名前の方が良いと思います。理想は短く伝わること(できるなら1語)です、より理想に近い名前づけを意識すると良いかと思います。(i
や id
などデファクトスタンダードな名前に関しては例外)
class Identifier {
String value;
Identifier(String value) {
this.value = value;
}
String value() {
return value;
}
}
出来るだけ独自型を使用する
String
や int
など言語にすでにある型は独自アプリケーションの概念を表現するには向いていないように感じます。よく変数などで命名して表現しようとしているコードを見ますが、変更が起きた場合にIDEの力を借りての変更ですら「変更忘れ」「Typo」など面倒なだけの作業になりかねません。また独自に型を定義することで得られるメリットは、引数を渡す際にコンパイルエラーになってくれる、プロダクトコード内に同じ概念のオブジェクトが別で定義されない(再利用)、など出来るだけ型で表現してみるのがいいと思います。
class Name {
String value;
Name(String value) {
this.value = value;
}
String value() {
return value;
}
}
名前が思いつかないときはわざと変な名前をつける
よく「こんなクラス名でPR上がってるのなぜ?」と言われるんですが、そう思わせたらほぼ思惑通りです。目指しているのは誰もが引っかかる違和感です。あとで変更する意図を名前に含めておくことで戒めのような活躍も変な名前はしてくれます。それっぽい名前をつけてしまうと、見向きもされずに腐っていくのがオチという体験を繰り返して私はこう書いています。本番にマージするには勇気がいりますが、よくよく考えてみてください。それっぽい名前、それっぽい処理が、この変な名前以外に絶対ないと言い切れますか?またそれを明確に見つけられる手段はあるのですか?と考えればそれほど抵抗は無くなります。一番大事なことですが最後にはしっかり名前をつけてことが、変な名前をつけた責任です。
// TODO 言葉を見つけられた時に修正する
class XxxCharacter {
String value;
XxxCharacter(String value) {
this.value = value;
}
String value() {
return value;
}
}
区分はとりあえずEnumで列挙してみる
趣味です。ある程度思考停止してます、と言いつつ区分にできるものは業務処理の分岐などになりやすく、区分による処理の違いを押し込めやすいためとりあえず定義してみます。合わなければ多分区分にするほどでもなかったと割り切って独自クラスとしてそれぞれ宣言するか、別の何かを考えます。
enum Gender {
NONE, MALE, FEMALE
}
業務ロジック(ビジネスルール)はモデルに持たせるように心がける
業務ロジックはレイヤー分けされた中のService層
などに良く散見されます。私が思うにService層
は業務を書く層で、ロジックを定義する層ではないと思っています。あるべき層に書くとしたらDomain層
に書きます。理由はいたってシンプルで、業務の関心ごとを詰め込んだ層だからです。私は業務の関心ごとの事を業務ロジックと思っています(勘違いかも)。ちなみにDRYも守れていいね!という観点も嬉しいです。
class Age {
int value;
Age(int value) {
this.value = value;
}
int value() {
return value;
}
boolean isMinor() {
return value < 20
}
}
完全コンストラクタでインスタンス生成する
不変を意識しています。インスタンス生成時にオブジェクトの責務を限定し、内部の状態が変更されないことを保証したいためです。Setter
を書かないことも関連してきます。可変を許容しやすいコードを書くと誰かにどこかで知らぬ間に変更されている!という状況を与えてしまい「コードを読む、テストをする、デバッグをする」等の作業の時に見なければいけないコードの量も増えていきます。見る箇所は最小限かつ状態定義する場所は一つの方がいいですよね?常に同じ状態が返ってくるということで、前述のようなことが起きないのはプロダクトが大きくなるにつれ、とても幸せだと感じるようになると思います。何気なくSetter
でクラスの状態を定義しようとした時は踏みとどまってみるのもいいかもしれませんね。厳密にはfinal
修飾子を付与し更に強固にしたほうがという意見ももらうのですが、プロダクトに関わるチームのレベル感によって合わせています。こんな使い方する人いないだろうをある程度信じて私はfinal
修飾子を書くことが少ないです、何より書くのが面倒なので。もっとよく知りたい人は完全コンストラクタパターンで調べてみてください。
class User {
Identifier identifier;
Name name;
Age age;
XxxCharacter xxxCharacter
Gender gender;
User(Identifier identifier, Name name, Age age, XxxCharacter xxxCharacter, Gender gender) {
this.identifier = identifier;
this.name = name;
this.age = age;
this.xxxCharacter = xxxCharacter;
this.gender = gender;
}
}
前がかりとしてstaticメソッドでインスタンス生成する
インスタンス生成に名前をつけることで抽象度が高いクラスの責務を定義します。抽象度の低い別クラスにするかの指標や、あるカテゴリ違いによるインスタンス生成を行う場合に使用してます。お決まりごととしては、コンストラクタをprivate
にするのをお忘れなく。でないと自由度が増してしまい想定外の状態を持つクラスが誕生してしまうことになるので。ちなみに理由なく何でもかんでもstatic
にしているとstaticおじさんといって嘲笑されかねないので気をつけてください。
class User {
Identifier identifier;
Name name;
Age age;
XxxCharacter xxxCharacter
Gender gender;
static User ofMale(Identifier identifier, Name name, Age age, XxxCharacter xxxCharacter,) {
return new User(identifier, name, age, xxxCharacter, Gender.MALE);
}
static User ofFemale(Identifier identifier, Name name, Age age, XxxCharacter xxxCharacter,) {
return new User(identifier, name, age, xxxCharacter, Gender.FEMAL);
}
private User(Identifier identifier, Name name, Age age, XxxCharacter xxxCharacter, Gender gender) {
this.identifier = identifier;
this.name = name;
this.age = age;
this.xxxCharacter = xxxCharacter;
this.gender = gender;
}
}
ループ処理の雑な使い分け
Javaでループする場合は主に、stream
かfor
を使用してます。
私が使い分けする雑な指標として、例外を投げない、あるオブジェクトからあるオブジェクトを生成するなどの時はstream
、例外を投げる、voidのメソッドを順次呼ぶだけなどの時はforを使用しています。stream
はメソッドチェーンで書けてオブジェクトに対する中間操作など複雑になりがちな処理をシンプルに見れるので個人的には好きです。一方for
はブロック内で直前の処理にあまり影響しないようなコードを書くときに使用します。あと例外がstream
より扱いやすいので。
util系のモジュールを作りそうになった時は踏みとどまってみる
util
、manager
、helper
など役目が広すぎて神になってしまうモジュールのことですね。「便利なツールってないかなぁ」や「変更の時大変やし、とりあえず重複避けたろ」というときに良く作られる傾向です。特に後者は変更がきた場合に、使用している片方だけに影響があるパターンが多いです。これをDRYとしてドヤられても困るなんてことは良くありました。良く考えてみてください、言葉通り神なので愚かな人類が扱える代物ではないのです。むしろ人が生み出す神なんて合成魔獣でしかないんです。疾う疾う成仏するべし。
コードをコメントで説明しようとした場合は、踏みとどまる
悪気はないはず...のやつです。コードのすぐ近くにありコード以上の情報を与えない、謎規則による謎フォーマットの価値がない、多分あげればたくさんあるのですが、あまりいいものではないかもしれません。コメントはコンパイルが通りません、間違っていてもコンパイラは教えてくれないので負債になりやすいです。コードは動くので間違っていれば指摘がありますし、IDEの力を借りればリファクタもやりやすいと思っています。私がコメントを書く際に考えるのは二つありまして、このコメントは善意になりうるか悪意になりうるか、これを書くまえにコードで表現できないかです。コメントが悪だとは思いませんが、道具は使うものによって道生(その後)を決められます。真摯に向き合ってみるのがいいのではと感じることが多いです。
テストコードにおいてはテストメソッド名は日本語で書いてコード内にコメント等を書いてみる
すぐ上で書いたのに、コメント書くんかい!と思われるかもしれません。しかしテストコードにはプロダクトコードと違い、決まった流れがあることは少なく実装者に委ねられることが多いので私的感情や論理が入り混じる書物になります。だとすれば説明の責任を取ろうという気持ちが私は強くなります。あ、関係ないですがテストは仕様書!と良く耳にしますが、果たしてそうなのでしょうか?と疑心暗鬼になる毎日です。
まとめ
今後も追加していく予定です。マサカリが怖いので私はと強く強調しています。ですが、良い悪い関わらずご意見あればよろしくお願いします。