yojikのlog

yojikのブログです

Java8でDuckTyping的なこと

Javaのラムダ式のついでに導入されたメソッド参照がなかなか便利な感じです。メソッド参照をつかってDuckTyping的なことができます。

HasName という関数型インタフェースを受け入れるGreeterクラスの定義*1
public class Greeter {
  /** 関数型インタフェースの定義 */
  @FunctionalInterface
  interface HasName { String getName();}
  
  /** 「名前を持っているオブジェクトに挨拶をしてもらう」メソッド */
  public void greet(HasName... targets) {
        for(HasName it : targets) {
            System.out.println(it.getName());
        };
  }
}
「HasNameの関数型インタフェースと同じシグネチャを持つメソッド」を持つクラスの定義

ただし引数と戻り値さえあっていれば、HasNameインタフェースを実装する必要もないし、名前をあわせる必要も無いです。

class Duck {
    public String name() {
        return "duck";
    }
}
class Dog {
    public String myname() {
        return "dog";
    }
}
Greeter::greetメソッドにメソッド参照を渡す

DuckやDogのインスタンスからメソッド参照を取りだしgreetメソッドに渡す。メソッドの引数戻り値さえあっていればどんなクラス/インスタンスのメソッドでも渡すことができます。

もちろんクロージャにおける「環境」のように、インスタンスの情報*2も一緒についていきます。*3

public class Sample {
    public static void main(String[] args) {
        Greeter  g = new Greeter();
        Duck duck= new Duck();
        Dog dog  = new Dog();
        g.greet(duck::name, dog::myname);
    }
}

*1:@FunctionalInterfaceアノテーションは無くても別にいいですが、書いておくと定義が関数型インタフェースに沿っているかチェックしてくれます

*2:この場合ならduckやdogのインスタンス変数の情報。定義してないけど

*3:メソッド参照に対応するインスタンスのクラスはSample$$Lambda$数字 という名前になるようです。なおコンパイル時ではなく動的に生成されている模様

DCIの疑問点をまとめる

以下勉強会(DCI hansei meetup)用のメモです。

自分の立場

問題意識は理解できる。しかしこの問題は以前から理解されていたものであり、それぞれの方法論等で常に問題になっているものである。DCIで解決策として示されているものが、それ以外の方法に比べて特にすぐれているとは思えない。

DCIに関する自分の理解

DCIとは

スピリチュアルなことを別とすればMVCと同様のアーキテクチャパターン

http://d.hatena.ne.jp/digitalsoul/20100131/1264925022

コンテキスト

ユーザーとの相互作用があるソフトウェアをつくる際には、ユーザの意図とソフトウェアの内部で起きていることを同期させる必要がある。具体的にはGUIアプリケーションの構築時など。

DCIが解決しようとしている問題

従来のMVVの道具(オブジェクト指向の道具)では、、いろいろな不具合がある!

ユーザのメンタルモデルをソフトウェアが表現できていない。

オブジェクト指向の道具(クラス、インスタンスインスタンス間で交換されるメッセージ)では、メンタルモデルの構造を表現すのには向いているが、「振る舞い」を正しくマッピングできない。

複数の小さなオブジェクトが分担してアルゴリズムを実行するというモデルはユーザのメンタルモデルから乖離し風雑さを増している。(余談ですがアルゴリズムという言い方は(ビジネスロジックという言い方より明確でいいですね))

どのオブジェクトが振る舞いのどの部分を分担するのかは常に明確じゃない。(新人研修のころ「サイコロ」をモデル化するために、「人間がサイコロを振る」「サイコロが転がる」のか一日論争になった記憶がよみがえります)

継承の問題点もろもろ

良くある話なので割愛

データモデルは安定し、アルゴリズムは安定しない。

したがってドメインクラスはデータの処理をおこなってはならない

ちなみに元の論文ではモデルの肥大化については特に語っていません。(間接的には関係あると思いますが)

フォース

  • マインドモデルの「構造」を捉えるのはオブジェクト指向の道具(ドメインモデルとか)が適している けど、
  • 振る舞いはむしろFortran時代のような手続き型、集中化された記述が適している

と、Trygve Reenskaug氏とJames O. Coplien氏 は考えている

この矛盾したフォースを調和させるために、、

解決策

システムの構成要素を以下のように分割する

  • 振る舞いを持たないデータ
  • 特定のユースケース(コンテキスト)に参加するオブジェクトの振舞いをまとめたロール
    • (技術的にはTraits等で実現する)
  • データをロールを紐つけるコンテキスト

特定のコンテキスト(ユースケース)実行時には、データに対して、そのデータがそのコンテキストで振舞うべきことを記述したロールを組み込み、アルゴリズムを実行させる。

ちなみにコンテキストはネストすることも可能

ここから俺の疑問です

実現手段としての「トレイト」の問題点

メンタルモデルとドメインモデルの乖離問題はよくわかる。しかし、そこで問題となるのは振る舞いとデータの乖離ではなく、粒度ではないのか。(トレイトでは解決しないのでは)

例えば論文のサンプルにあった預金口座オブジェクトに対応するデータ構造はふつうの銀行関連アプリケーションにはないだろう 。通常はトランザクションや監査ログなどの細かいデータの集合として実現される(論文でも触れていますが)

トレイトとしてロールを表現する場合、対象となるデータオブジェクトに対して振る舞いを組み込むのみ。メソッドが増えたり減ったりするのみ。 粒度の違いについては解決しない。

上記の例だと口座を構成する複数のデータのうちどれにロール被せればいいのかわからない。

問題を記述するには細かすぎるオブジェクトを集約して大きな粒度であつかう技法が必要なのでは

  • アダプタやサービス、ファサードを使った間接層をはさむとか
  • 集約ルートを使ってデータ(的)オブジェクトよりは大きな単位で処理を記述するとか
  • SQLアンチパターンではActiveRecordを持つドメインモデルを導入(永続データと処理を行なうオブジェクトの分離)
  • OOSEにおけるBCEも少し関係するかも

上記方式ではなく、トレイトを使うとオブジェクトの同一性が保たれるのがメリットとされるが、なぜメリットなのかよくわからない。

※振る舞いの再利用手段としてのトレイトは否定しません。たとえば、永続化機能がないドメインオブジェクトにActiveRecordトレイトを組み込むとか、そういう用途に便利そう。

データオブジェクトがコンテキスト共通であるという誤解

特定にコンテキストしか使わないデータオブジェクトというのは確実に存在する(つまりデータのレベルでもコンテキストに依存するものがある)

結局データの階層がシステムでもっともロバストだというのは幻想である。データの中にもシステム共通のもの、それぞれのコンテキスト依存のものがあり、保守のライフサイクルが異なるはず。ライフサイクルが異なるものはパッケージやレイヤなどで分離した方がいい。

またドメインモデル、データモデルの段階でロールっぽいものを抽出する方法論も存在する。データだからコンテキスト共通ということはない。。

例:

  • UML in Color (Peter Coad)におけるロール
    • ドメインモデルを分析する段階で、ロールオブジェクトを抽出し、共通のドメインオブジェクトに集約させる (DCIとは依存の)
    • 「ロール」は「パーティ/場所/物)」がイベント(瞬間時間間隔)にどのように参加するかを示す

その他: T字系のVirtualEntityとか(自信なし)

コンテキストが複数ある場合のパターンが示されていない

元論文ではコンテキストを別のコンテキストから利用するというケースが想定されている。しかしその実現手段については述べられていない。コンテキストはマクロな目でみればデータ(状態)と振る舞いを持つふつうのオブジェクトであり、これらが協調動作する、というのは結局一周し元のモデルに戻っているのでは。

WDYT?

SQLアンチパターンを読んだぜ!

どうしようもない僕に献本が降りてきた!

SQLアンチパターン

SQLアンチパターン


ありがとうございます。

結論を先に述べると、すごく読みやすくてためになる本です。

RDB使ったシステムを作っている人、特に長年やって人ならば必ず遭遇したことがあるアンチパターンのカタログです。アンチパターンが発生しやすい状況、悪影響や解決方法なども述べています。若い人は現実のシステムで失敗する前に予習できるので、絶対買ったほうがよいでしょう。「この問題!進研ゼミでやったところだ! 」

自分は論理設計のアンチパターンのあたりがお気に入りです。アンチパターンポリモーフィック関連」や「エンティティ・アトリビュート・バリュー」や「マルチカラムアトリビュート」をクラステーブル継承や従属テーブルをつかって回避するのはかなり好きです。*1

本書にはアンチパターンを適用しても良い状況の説明もあって好感が持てます。この本では自然キー(大抵は複合主キーとなる)を推奨しているのですが、ORマッパー利用を前提とすれば、擬似キーを主キーにすることを認めています。この本は単なる「べからず集」ではなく「パターン本」だからです。コンテキストや制約が異なれば導かれる解法も異なるというわけです。

また、後半に出てくるアンチパターン「モデルがアクティブレコードそのもの」に対する解法「モデルがアクティブレコードを持つようにする」については、最近のDCIとの関係でもう少し深く考えてみたいところです。「Entiryが複数コンテキストから使われることによって肥大化する」はDCIが解決しようとする問題の一つだと思うのですが、新しいパラダイムを導入するまでもなく、このパターンを導入すれば済むようなケースも多いんじゃないかと思います。こちらに関しては後日別エントリで。

*1:自分に口を出す権限があれば、いつだってやりたい。OOでいう継承や集約を利用したパターンと利用感覚が似ている