1 of 17

Dart言語仕様 Pick-up

tomochikahara

@zetta1985

2 of 17

Agenda

  • Method Cascading

  • Named / Factory Constructor

  • Const Variable

  • Implicit Interfaces / Mix-in

3 of 17

Method Cascading

4 of 17

従来のMethod Chainへの批判

  • クエリ・コマンド分離原則に反する
    • 状態を変えるメソッドなのに戻り値がある
    • 戻り値がCalleeと同じ参照であることをシグネチャが表現していない

  • 専用のシグネチャが必要
    • ライブラリ提供者の意図を汲む必要がある
    • 専用のシグネチャを持たないメソッドはMethod Chainできない
    • 変更に弱い

※ Method Chain != Fluent Interface

5 of 17

Method Cascadingでの解決

  • 全てのメソッドをMethod Chainできる
    • 戻り値を捨てる
    • 代わりにMethod Cascading式で返される値がCalleeになる
    • 専用のシグネチャを定義する必要無し
    • Method Chainするか否かはライブラリ利用者が自由に決める

  • 使い道
    • DOM APIをjQuery-like
    • 複数の副作用メソッドの呼び出しを一つの式

6 of 17

Method Cascadingの例

var tokai = (new StringBuffer("[")

..writeAll(["Aichi", "Gifu", "Mie"], ",") ..write("]")).toString();

query("#sample_text_id")

..text = "Click me!"

..onClick.listen(reverseText);

var person = (new PersonBuilder()..name = "to_hara"..age = 13).build();

7 of 17

Named / Factory

Constructor

8 of 17

Constructorの基本

class Person {

final bool isAdult;

Person(this.isAdult);

}

class Person {

final bool isAdult;

Person(int age) : this.isAdult = age >= 20;

}

class Person {

bool isAdult;

Person(int age) : this.isAdult = age >= 20 {

assert(age < 20 == !isAdult);

}

}

仮引数の定義とフィールドの初期化

フィールド初期化式リスト

(コンストラクタのリダイレクト含む)

オブジェクト初期化子

(finalフィールドへの初期化はできない)

9 of 17

Named Constructor

(名前付きコンストラクタ)

  • コンストラクタに任意の名前を付けられる
    • Dartは動的言語なので、パラメータの型によるコンストラクタのオーバーロードができない
    • 複数のコンストラクタを定義するときは名前を付ける

class Person {

final String name;

Person(this.name);

Person.copy(Person original) : this.name = original.name;

}

var hara = new Person("Hara");

var hara2 = new Person.copy(hara);

10 of 17

Factory Constructor

  • コンストラクタの実装をFactory Methodで
    • クライアントからはGenerative / Factoryの違いはわからない
    • インスタンスのキャッシュ、パラメータから実クラスの選定、DIコンテナへの委譲など

abstract class Person {

final int age;

Person._internal(this.age);

factory Person(int age) => age < 20 ? new Young(age) : new Adult(age);

}

class Young extends Person { Young(int age) : super._internal(age); }

class Adult extends Person { Adult(int age) : super._internal(age); }

11 of 17

Const Variable

12 of 17

変数のインライン展開

  • コンパイル時にオブジェクト生成式を評価
    • 言語仕様によるFlyweightパターンの実装
    • 動作時の不変性が保証される

  • 用途
    • 不変性の明示?パフォーマンス?
    • ぶっちゃけ用途がわからない・・・
    • finalではインライン展開されないことを厳密に既定しかった?
      • Javaはプリミティブなfinal変数がインライン展開される。外部jar内のプリミティブなfinal変数を参照、コンパイルした後、外部jar内のプリミティブなfinal変数の値を変えた場合、コンパイルしなおす必要がある

13 of 17

Const Constructor

  • const初期化を可能にするコンストラクタ
    • 要求(制限)
      • 全てのフィールドにfinalがついていること
      • 全てのフィールドの値がconst初期化できること
      • オブジェクト初期化子が記述されていないこと
      • Factory Constructor ではないこと

class Location {

final String name;

const Location(this.name);

}

var l3 = const Location("Aichi");

var l4 = const Location("Aichi");

print( identical( l3, l4 ) ); // true

14 of 17

Implicit Interfaces

/ Mix-in

15 of 17

Implicit Interfaces

(暗黙的Interface)

  • あらゆるクラスが暗黙的にインタフェースをもつ
    • インタフェース=Java等と同じ
      • 型名とメソッドのシグネチャのみが定義される

class Person {

final bool isAdult;

Person(int age) : this.isAdult = age < 20;

}

class Young implements Person {

// getterのオーバーライド

bool get isAdult => false;

}

16 of 17

Mix-in Application

  • クラスに振るまいを追加する
    • 任意のクラスをMix-in可能にするための制限
      • コンストラクタが定義されていないこと
      • Objectクラスを継承していること
      • super呼び出しがされていないこと

class Printable {

void printMe() => print(this.toString());

}

class Person extends Object with Printable {

final int age;

Person(this.age);

String toString() => "I'm $age years old.";

}

17 of 17

Implicit Interfaces & Mix - inの実用例

Dependency Injection : Cake Pattern

class Repository {

List<String> find() => ["data1", "data2"];

}

class Service {

Repository get repository => new Repository();

void execute() => print(repository.find());

}

class AwesomeRepository implements Repository {

List<String> find() => ["awesomeData1", "awesomeData2"];

}

abstract class AwesomeRepositoryModule {

Repository get repository => new AwesomeRepository();

}

typedef AwesomeService = Service with AwesomeRepositoryModule;