送金メソッドを考える

これは例だが、口座Aから口座Bへお金を送金するといった処理をどのように書くかで悩んでいる。
送金だけでなく、ユーザーAがユーザーBをフォローするといった操作でも同じように困る。

何に困るかと言うと、引数の取り違えが起こりそうだからなんとかしたいなと思っている。

前提

今回の話で前提としているのは3層+ドメインモデルのアーキテクチャで以下のリポジトリを参考にしているので参照してみてほしい。

github.com

application層に送金メソッドを作って、ControllerやBatchなどから送金を依頼されるというユースケースを考えている。
愚直に考えるとこんな感じだろうか。いろいろ省略して書いているのであしからず。

@PostMapping("transfer")
public String transfer(@RequestParam long amount, @RequestParam long toAccountId, User user) {
  BankAccount fromBankAccount = bankService.findByUser(user);
  BankAccount toBankAccount = bankService.findById(Id.of(toAccountId)):
  TransferAmount amount = new TransferAmount(amount);

  // ここでfromBankAccountとtoBankAccountの順番を間違えてもコンパイルエラーにならない。間違えやすい!
  bankService.transfer(fromBankAccount, toBankAccount, amount);
  return "...";
}

ましな案を考える

FromBankAccountとToBankAccountの型を分ける

@PostMapping("transfer")
public String transfer(@RequestParam long amount, @RequestParam long toAccountId, User user) {
  FromBankAccount fromBankAccount = new FromBankAccount(bankService.findByUser(user));
  ToBankAccount toBankAccount = new ToBankAccount(bankService.findById(Id.of(toAccountId))):
  TransferAmount amount = new TransferAmount(amount);

  // 型で意図が分かりやすいので間違えにくくなる…?
  // FromBankAccount作るときに間違えるかもしれないけどまだマシかも。ラベル引数できればよかったのにね。
  bankService.transfer(fromBankAccount, toBankAccount, amount);
  return "...";
}

ちょっと面倒だけど、型を作ってラベルとしての役割をもたせてみる。
順番でやるよりはコードを見たときに「あれ?これミスってね?」と気付けるチャンスが増えているのでまぁマシ。

TransferRequestを渡す

TransferRequestを作って送金依頼をするパターン。よさ。
よりラベル感があって間違えにくいかも。

@PostMapping("transfer")
public String transfer(@RequestParam long amount, @RequestParam long toAccountId, User user) {
  BankAccount fromBankAccount = bankService.findByUser(user);
  BankAccount toBankAccount = bankService.findById(Id.of(toAccountId)):
  TransferAmount amount = new TransferAmount(amount);

  bankService.transfer(TransferRequest.builder().from(fromBankAccount).to(toBankAccount).amount(amount).build());
  return "...";
}

BankAccountにtransferメソッドを生やす

EntityやValueObjectができる操作は限られていて、主にできることは計算/判断/変換でDBを叩いたりWebAPIを叩くことはできない。(ActiveRecordみたいに何でもやるマンにすると仕事し過ぎかなってきがする。
とはいえそう呼び出したい気持ちがあるので、TransferRequestを作るメソッドを生やす案を考えてみた。

public class BankAccount {
  public TransferRequest transfer(BankAccount toBankAccount, TransferAmount amount) {
    return new TransferRequest(this, toBankAccount, amount);
  }
}

Controllerでの呼び出しはこんなかんじだろうか。

@PostMapping("transfer")
public String transfer(@RequestParam long amount, @RequestParam long toAccountId, User user) {
  BankAccount fromBankAccount = bankService.findByUser(user);
  BankAccount toBankAccount = bankService.findById(Id.of(toAccountId)):
  TransferAmount amount = new TransferAmount(amount);

  // 送金依頼を作って送金する
  TransferRequest request = fromBankAccount.transferTo(toBankAccount, amount);
  bankService.transfer(request);
  return "...";
}

なんかアリな気がしないでもない。筋は良さそう?

引数を非対称にする

送金元はEntity、送金先はIdオブジェクトとすることでミス防ごうという案。

@PostMapping("transfer")
public String transfer(@RequestParam long amount, @RequestParam long toAccountId, User user) {
  BankAccount fromBankAccount = bankService.findByUser(user);
  Id<BankAccount> toBankAccountId = Id.of(toAccountId):
  TransferAmount amount = new TransferAmount(amount);

  // 送金依頼を作って送金する
  bankService.transfer(fromBankAccount, toBankAccountId, amount);
  return "...";
}

これは面白いかもしれない。クラスを作るのが面倒なときとかの妥協案として選ぶのはアリ寄りのアリかもしれない。

結局

どれがいいんでしょうね?BankAccount#transferのパターンは結構筋良さそうな気がする。直感だけど。

クラスを作ることをサボらない

JJUG CCC 2018 Fallのいくつかのセッションで型についての話があったので書きたくなった。
この話はJavaScalaみたいな静的型の世界の思想で、多分Rubyとかでは違う思想だとおもう。

Javaを書いてるときのおれきゅーの脳内

型を定義して取りうる値を狭めることで 間違った使い方ができないようにする ことを最初に考えてる。多分。防御的プログラミングってやつなのかな?しらんけど。
とにかくエラーはより早いタイミングで見つかるほうが良くて、実行時よりコンパイル時にわかるほうが便利だし、コンパイル時よりエディタ上でリアルタイムに分かるほうがもっと良い。

たとえば汎用的な型を使うとこんなミスがあり得る。

long id = 10;
new User(id);
new Item(id);

静的型なら多分こうかけるほうが間違えなくて嬉しい。

Id<User> id = Id.of(10);
new User(id);
new Item(id); //コンパイルエラー

型に意図をもたせる

これはirofさんのセッションに出てきた話で、突然String strという変数が出てきたとき、これが何なのか、どんな操作が許されているのかがわからない。Stringという型には文字列以上の情報が含まれていなくて、splitしたりsubstringできたりする。仮にこれがDisplayNameという型であれば、splitやsubstringなんて操作はないし、表示用に整形するだとかDisplayNameとしての操作だけが提供されて間違えることはない。
コンストラクタやfactoryメソッドで入力値をチェックすることで例えば空文字の場合はインスタンス化できないようにすると、DisplayNameの取りうる値の範囲が制限されてDisplayNameを扱うときに考えなければいけないことを減らすことができる。

Railsにはcomposed_ofというあまり使われていないマイナーな機能があってそれを好んで使っている。これはDBのカラムの値を値にマッピングする機能で、JPAでいうEmbeddedと同じような機能。Rails界隈であまり使われていない機能でも好んで使うのは取りうる値の制限ができるという理由から来ている。
宣伝ですが、composed_ofについては技術書典5で頒布した「pixiv PAYの薄い本」で書いてある。ちなみに表紙は僕が一番好きなイラストレーターのしらたま先生です。

booth.pm

Folioという証券会社のScalaの事例では、金額などの計算に型を使うことでうまく意図をもたせていた。

val yen = JPY(100)
yen * 5 // JPY(500)
yen * yen // JPY同士の掛け算は定義していない。そんな意味不明な計算はありえない。
yen / 5 // 割り算とかもありえないのでコンパイルエラー

これはわかりやすい例なのでそんなことでミスしないだろうと思いがちだが、ドメイン知識が複雑になると力を発揮する。
別の例で、損益と損益の引き算の例では以下のようなコードが示されていた。

/** 実現損益と含み損益をあわせて損益 */
case class PnL(unlialized: UnlializedPnl, realized: RealizedPnl) {
  /** 損益の差は損益ではなく損益の差 (例えば前日比)*/
  def -(other: PnL): PnlDiff = PnlDiff(this.value - other.value)

  def value: JPY =  unlialized.value + realized.value
}

計算結果の型によって計算の意図が読みやすくなるし、型が明確に分かれることで取り違いがなくなったり、気をつけたり意識することの脳内リソースを開放できる。

クラスはどれだけ作れば良いか

別の概念のものなら息をするように作る細かく分ける。
名前考えるのは面倒だけど、作るのは一瞬。ずっと使うものだしどんどん作ろう。プリミティブは滅ぼそう。
とにかく最初はやりすぎかな?って思うくらいやればいい。分けるのは大変だけど統合は簡単。しらんけど。とりあえずやってから考えよ。

おれきゅーのコードを書くときの心構え

息をするようにクラスを作る。名前を決める。
クラスを作るのをサボらない。


参考

コードをどまんなかに据えた設計アプローチ - Speaker Deck

Scala と Microservices でつくる 証券会社とスタートアップ / FOLIO in JJUG CCC 2018 Fall - Speaker Deck

Retrofitの仕組みを知ろう

はじめに

Javaアドベントカレンダー5日目担当の null です。
Javaのライブラリやフレームワークで登場するアノテーションは、メソッドやフィールドにつけるだけで値が勝手に入ってきたりして魔法のように思うかもしれません。今回はHTTP ClientのライブラリであるRetrofitを題材にアノテーションがどのように使われているか調べてみましょう。

Retrofitのざっくりした動き

ドキュメントのサンプルからコードを引用します。
ライブラリの利用者はGitHubAPIを叩くインターフェースを用意します。

public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

Retrofit#createにインターフェースのClassクラスを渡すとインスタンスが取れます(!)

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

GitHubService service = retrofit.create(GitHubService.class);

以下のようにGitHubServiceのメソッドを呼び出すとなんとAPIを叩いて結果を返してくれます。

Call<List<Repo>> repos = service.listRepos("octocat");

実装を用意していないのにインスタンスが作れてしまったり実装ができていたりして不思議ですね!

魔法の正体を追う

Retrofit#createの実装を覗いてみましょう

github.com

    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();
          private final Object[] emptyArgs = new Object[0];

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
          }
        });

Proxy.newInstanceが怪しそうですね!Java SEのクラスなのでProxyクラスのJavadocを読んでみましょう。
Proxy (Java SE 11 & JDK 11 )

Proxyクラスはプロキシクラスというインターフェースのインスタンスのように振る舞うインスタンスを作るメソッドを提供しています。第二引数のインターフェースを実装したプロキシクラスを作り、プロキシクラスのメソッドが呼び出されると第三引数のInvocationHandlerのinvokeメソッドが呼び出されます。

InvocationHandler#invokeにはプロキシクラスのインスタンス、呼び出されたMethod、メソッドに渡された引数が与えられるので、リフレクションを駆使しつつ処理を行い結果をreturnすればRetrofitの完成という仕掛けでした。

その他のアノテーションが使われる場面

アノテーションは今回のようなリフレクションで使われるだけでなく、アノテーションプロセッシングというコンパイル時にコードを生成する仕組みでも利用されます。
代表的なライブラリだとLombokDomaなどが有名だと思います。アノテーションプロセッシングはリフレクションと違ってコンパイル時に処理するためエラーをより早いタイミングで警告することができるメリットがあります。

おわりに

Retrofitを通じて魔法のようなアノテーションの裏側の仕組みの理解が進むと嬉しいです。
明日は@hishidamaさんの「Asakusa Frameworkと次世代データ処理基盤技術」です。
いつも灰色のページお世話になってます!ありがとうございます!

お絵かき入門

はじめに

エンジニアをやりつつ絵を描き始めてからもうすぐ2年目になるし、上達してきた感じがするので2年前の自分と同じくキャラクターの絵を描きたい!という人のために情報をまとめてみようと思ったので、やってみたいぞ!という人は参考にしてもらえると!
ちなみに2年での成長はこんなかんじ。 f:id:orekyuu:20181123172744j:plainf:id:orekyuu:20181123172752j:plain

対象

絵を描き始めたいけど何から手を付けていいかわからない人

道具を揃える

ペイントソフトとペンタブor液タブが必要。
ペイントソフトはCLIP STUDIOSAIがあります。好みで選べば良いと思いますがCLIP STUDIOは機能が豊富で、SAIは描き味が良いというイメージです。ちなみに僕はCLIP STUDIOを使ってます。
pixivのプレミアムになればCLIP STUDIOがもらえるので、プレミアム会員の人はそれを使っても良いと思います。

ペンタブに関しては大体ワコムのものを使っておくと良いと思います。液タブはすごくお値段がするので、最初は手を出さないほうが良いかと思います。ipad proがあればそれでやってみるのもいいかも。

キャラの描き方を覚える

いくつか入門サイトがありますが、最初はpixiv senseiのキャラクターコースから始めました。特に顔の基本コースをずっと頑張るって感じですね。これだけでは結構難しいところもあるので入門書の力も借りましょう。
おすすめの入門書はヒロマサのお絵かき講座<顔の描き方編>です。ヒロマサ先生のシリーズはかなりわかりやすく解説してあるのでおすすめです。
ある程度顔の描き方を覚えると髪や目の描き方が気になってくると思うので、tips系のサイトを見ながらやっていくと良さそうです。

自分が見てたサイト

ichi-up.net

tips.clip-studio.com

好きな絵描きさんの絵を観察する

好きな絵描きさんを目標に、どうやったら髪が柔らかく見えるか、顔はどんな輪郭になっているか、パーツのバランスはどうかを観察しながら自分の中で噛み砕いて絵を描いてました。

絵を投稿してモチベーションを保つ

はじめの頃はリアクションがもらえなくて辛かったので、リアクションが貰えそうなところに絵を投稿しました。
Twitterやpixiv Sketchですね。とくにpixiv Sketchははじめの頃でもリアクションがもらえるのがすごい嬉しくてモチベーションに繋がりました。

最後に

続けてたらなんか成長するので最後は継続力なのかなーと思います。あとはpixiv sketchのライブなどで上手い人がどうやって描いてるか見ると勉強になるかも!とか思いました。
こういう絵描き系の記事って需要あるのかな?w
気が向いたらまた書くかもしれないです