Angular2 における Injector の話

ちょっと機会があってみんなで Angular2 + TypeScript TUTORIAL: TOUR OF HEROES を少しずつ読んで行く勉強会をやっているのだが、コンポーネントの依存性解決に関してかなり独特な世界観があったのでちょっとまとめる。

Dependency Injection (依存性の注入)って何?

依存性とは

あるクラスに特定の変数や定数、インスタンスが入ってしまっている状態。

class Car {
    engine:Engine = new HondaEngine();
    tire:Tire = new BridgestoneTire();
}

こんな例だと「この車はHONDAのエンジンとブリヂストンのタイヤに依存している」と言える。

これだと何が問題になるか

Car クラスの動き(特定のメソッド)をテストすることを考える。

  • HONDAのエンジンとブリヂストンのタイヤを用意しなければならない。
  • もしHONDAのエンジンを用意するのに10分かかると、このテストをする度に10分待たなければならない
  • テストする度にタイヤが摩耗して減ってお金がかかる場合、タイヤ回す目的でないテストにお金がかかることになる。

などいろいろ不都合がある。
実際の開発だと「ソシャゲの課金部分を実際にお金払う仕組みに依存したらテストが有料化した」みたいなことになりかねない。

注入ってなに

メソッドの引数で、クラスや変数などを外から受け取れるようにする

class Car {
    engine:Engine;
    tire:Tire;
    constructor(_engine: Engine, _tire: Tire) {
        this.engine = _engine;
        this.tire = _tire;
    }
}

こうすると、エンジンをTOYOTAにしようが、タイヤをピレリにしようが、それをコンストラクタの引数に指定することで車が組み立てられる。この過程が注入と呼ばれる。
こうして、この車が特定の部品に依存することなく走り出すことが出来るようになる。 テストする時も、理想的なエンジンのフリをする偽エンジンと理想的なタイヤのフリをする偽タイヤを用意することで、本当に Car クラスだけの挙動を見ることができる。
そのとき、もしも不具合が起こっても「エンジンやタイヤが悪いのかも」という心配をしなくて良くなる。

Angular の Dependency Injection

さて、次は Angular2 における Dependency Injection だが、実のところ チュートリアル ではいくつかある機能のうち1個しか使っていない。
まず使ってるものに関して説明する。

@Injectable() と Angular 内部のコンテナ

まず Angular の内部にはコンテナがあり、他のコンポーネント注入 するものを入れておくことができる。
前述の Car クラスだと、エンジンやタイヤのことで、チュートリアルだと HeroService が当てはまる。
@Injectable() でデコレートしたものを、Angularが内部コンテナに取り込んでくれる。

provider プロパティと Angular 内部のコンテナからの注入

@Component({
    ...
    providers: [HeroService],
    ...
}
export class AppComponent {
    constructor(_heroService: HeroService) { }
}

こんなコードで HeroService を Angular 内部のコンテナから取り出して AppComponent に注入したと思う。
これは実は下記のシンタックスシュガー(糖衣構文。ざっくり言うと省略記法のこと)である。

@Component({
    ...
    providers: [
        new Provider(HeroService, {useClass: HeroService})
    ],
    ...
}
export class AppComponent {
    constructor(@Inject(HeroService) _heroService: HeroService) { }
}

Provider とは何ぞ?

このクラスは、指定された何か(ここではHeroService)をコンテナからコンポーネント(AppComponent) に「どうやって注入するか」を管理するクラスだ。

new Provider(A, {useClass: B}) という形で説明すると

  • A: この Providerインスタンスに付ける名前(DIトークンと呼ばれる)
  • B: 注入するクラス名

になる。useClass の部分はいろいろ変えることができて、よく使うのは下記の3個くらいだと思う。
これらのうち、どれを指定するかによって、最終的に注入されるものや方法が変わる。

  • useClass:クラス名 指定したクラスからインスタンスを作って(new)注入してくれ
  • useValue:インスタンスやオブジェクト 指定したインスタンスやオブジェクトを注入してくれ
  • useFactory:関数 指定した関数から返される値を注入してくれ

ここで、useClass 意外のものを指定してるプロパティがクラス名でないということに注目してほしい。
実は、インスタンスじゃなくて変数や定数も注入できるし、関数を指定してその結果を注入するなんてこともできる。 ここで、同じProviderからは同じインスタンスやオブジェクトが必ず返されることを覚えておくといい。 useClass を使ったプロバイダを親コンポーネントと子コンポーネントから参照しても二度 new されることはなく、 Provider 内部にキャッシュされたものを参照する。
コンポーネントProvider がなければ、親コンポーネントProvider を参照するので、子コンポーネントに同じインスタンスを注入できる、ということだ。

@Inject とは何ぞ?

@Inject(HeroService) デコレータは、上で指定したDIトークンを指定して、 どの Provider から注入してもらうかを指定するものだ。

なんで省略できたの

  • Provider : そういう決まりだから。基本的に providers にクラス名だけ書いたら、DIトークンがクラス名と同じで、かつ useClass:クラス名 を指定したことになる。
  • @Inject(HeroService) : こっちには世界観というより的確な理由がある。DIトークンが引数のクラス名と同じである場合に省略できる。

やってみるといいこと

チュートリアル に関して、以下のようなことをして動きを確認すると良い。

  • 省略記法を Provider@Inject を使った書き方に変えてみる
  • DIトークンを変えてみて、正しく動くことを確認してみる
  • HeroServiceuseValue プロパティを使った書き方に変えてみる

寿司のスポンサーになりました

何言ってんのお前

Unicode コンソーシアムには里親( Adoptation )制度があり、スポンサーになると文字を一つ選択することで里親になることができる。選択した文字は Unicode コンソーシアムのサイト上 に自分の名前とセットで掲載してもらえるのだ。そこで僕は🍣を選び、1年間の期限付きではあるが🍣の里親、ブロンズスポンサーとなったのである。

どうして

"はじめに言葉ありき"というように、人間はそれを指し示す言葉がなければそれを認識できない。ただそれを認識したときに人がどうそれを受け入れるかというのはかなり怪しいもので、Unicode という統一を目指す文字コード体系は最終的に言語以上に文化の壁に当たるのではないかと思っている。実際、人アイコンの肌色で揉めたりとかは事実それなりに起きているし、今「犬を食べる」という絵文字や「鯨を捕まえて食べる」みたいな絵文字が入るとしたら、それを見たあらゆる人がそれを穏やかに受け入れてくれるなんて楽観的な考え方にはなれそうにない。Unicode コンソーシアムの仕事とは、言語の壁、文字の壁、そして文化の壁を世界中から取り払うことそのものであり、到底簡単なものではない。そしてそんな中🍣という文字があることにより、🍣の存在が世界から認知され、受け入れられ、長く存在できるということに対して何らかの貢献がしたかったとかそういうことはあまり考えていなくて、認定アイコンに🍣が乗ったら回転寿司の皿っぽくて面白いな、と思ったからだ。

スポンサー認定までの流れ

登録と支払い

  • Unicode コンソーシアムの Adopt ページ にアクセスする。
  • スポンサーになりたい文字を1個だけ選択して書き込む。文字そのものでもいいし、コードを書いてもよい。
  • 自分の名前を書く。ここに書いた名前をサイト上に掲載してもらえる。
    • ここに Twitter のスクリーン名を含めると、 @unicode がスポンサー告知ツイートをするときにリプライになるため便利である。いくつかそうしている人がいて大変賢いと思った。
  • スポンサードするレベルを決めます。
    • ブロンズ($100 - 無制限枠) : 希望すれば紙の感謝状が貰える
    • シルバー($1000 - 1字につき5枠) : ブロンズ特典に加え、希望すれば記念品がもらえる
    • ゴールド($5000 - 1字につき1枠) : シルバー特典に加え、メールで連絡すればサイト上の名前にリンク付けることができる
  • 希望するオプションを埋めたら Adopt ボタンを押して、メールアドレスやクレジットカード情報を入力する。

そのあと

  • 支払い直後にレシートメールが来る
  • 1営業日でサイト上に追加される
  • 2営業日で告知ツイートされる
  • と同時にコンソーシアムからお礼のメールが届く
    • そこにはブロンズ🍣スポンサーの透過pngアイコン(上記告知の画像と同じもの)が添付されている
  • 多分しばらくすると紙の感謝状が届く

まとめ

🍣だけにネタになるので、今のうちに乗っかっておくと面白いのではないかと思う。その際はぜひ🍣のスポンサーになって、届いた認定証画像をTwitterのアイコンに設定して、TLを回転寿司にしていきましょう。

追記(2018/06/27)

1年が経過した時点で再度スポンサーの登録申請をしたのですが、 Unicode コンソーシアムのサイト上で初期申請分がいつまでたっても消えず、また送られてくる感謝状から初期申請時にはあったスポンサー期間の記載が消えていました。 スポンサー期間は実質無期限になったと言えるでしょう。しかし、そもそも同一人物による同じ文字の再申請という狂った事例が私と @mecab氏 しかないので、これが特別な対応でないとは言い切れないです。

イカとインターネット

これは何

イカをきっかけにかなり回線周辺を強化したので、それらについてちゃんとまとめます。

家の外のネットワーク

図1:概略図 f:id:Dolpen:20160229172646p:plain

どんなISPを選んでも大体こんな感じです。上から順に話をします。

まずIXって何よ

ISP が加入する上位の ISP みたいなものです。この IX も国内外にたくさんあり、それらが相互に接続してインターネットはできています。ISP ごとに契約も違って、IX と ISP を繋ぐ回線帯域も異なっています。ほとんどの場合、インターネットへのアクセスは IX を通して行われるので、この IX と ISP を繋ぐ回線帯域が細いと、 ISP の利用者が多い場合混雑しやすい、というのはなんとなくわかると思います。そういう ISP をクソとかしょぼいとか言うわけです。

日本だと JPIX とかがメジャーです

JPIX:ホーム

上記 JPIX への加入業者一覧を見ても分かるように、Google とかの巨大インターネットメガコーポは、自社データセンターなどのインフラを ISP を通すことなく、この IX と直接契約してインターネットにサーバーを接続しています。

回線業者の選定

というわけで、我々はしょぼくない ISP を選ぶ必要があります。改めて整理するとこんな感じです。

  • ISP 同士を繋ぐバックボーン(図1-1)が太いか(別 ISP のクライアントや国外との通信に影響、ISP によって契約帯域が違う)
  • ISP 自体の基幹ネットワーク(図1-2)が太いか(同 ISP 内のクライアント間の通信に影響)

とは言っても、回線をとっかえひっかえして個別に試すことは出来ないので
BNR統計情報 2017/10/01 - 2018/03/01
などの統計情報から当たりを付けておきましょう。

他にも、

  • 昼間は良いけど休日や夜間は遅くなる(家庭用のユーザーが多く、帰宅時間帯になると基幹ネットワークがパンクしやすい)
  • 回線障害が多かったり、サポートが雑

などのサービス品質に関する評判も調べておいた方が良いです。

良い場所に住む

現代科学ではどんな回線でも光の速さを越えることはできないので、そもそも情報を移動させる距離(図1-1〜3)を短くする、というアプローチは有効です。
ISP が決まったら、その ISP が接続している IX の拠点を調べておきましょう。東京都内であれば大手町や日本橋周辺、および品川などが多いと思います。
特定のホストに ping や traceroute などの問い合わせを送信し、その結果が帰ってくるまでの時間を RTT (ラウンドトリップタイム) と呼びますが、23区内など、なるべく IX に近い回線収容拠点に接続出来る場所に住むことで、 RTT が 5ms を切って 3〜4ms まで改善することが期待できます。多分ここら辺が物理限界です。

家の中のネットワーク

図1-4 以降の話です。

ブロードバンドルーターの選定

回線契約時によく送りつけられるブロードバンドルーターは結構な割合でゴミがあって、回線自体は 500Mbps くらい出るのに機器が 100MbE までしか対応してなくて遅い、みたいなことがあるので、ちゃんとしたのを買いましょう。
適切な GbE 対応のルーターを買って、回線契約時の書類に書かれた PPPoE の設定をしてしまえばよいです。ヤマハでもシスコでもバッファローでもかまわないです。

端末接続

次に端末接続ですが、無線はあまり考えないで、1000BASE-T 対応の物( NIC とか アダプタ とか ケーブル)をなるべく用意してください。なるべくとは言っても、WiiUの有線アダプタなどは 100Mbps が限界なので買いようが無いです。。。
そこそこちゃんとしているルーターであれば、どの規格でルーターと端末が接続しているかを見てくれるものもありますが、各機器はそこまで高くないはずなので、分からなければ買っちゃうのも手です。
ケーブルであれば cat5e 以上であれば GbE にも対応するので問題ないです。

ネットワーク設定

少なくとも有線機器は固定IPにしましょう。DHCP で IP アドレスを振られるがままにしてしまうと、

  • なにか障害があったときに調べづらい
  • DHCP には IP アドレスのリース(割り当て)期間があり、期間を過ぎると再割り当ての間ネットワークが瞬断する

など問題が起こります。

あと大事なのが MTU の設定です。 MTU というのは、パケットを送受信するバイト数の単位で、これをネットワークに接続する全機器で同じにしておくのは、パフォーマンスの改善に繋がります。WiiU は 1500 バイトずつパケットを送受信して、一方でルーターの送受信単位が 1400 バイトであるような不一致があると、ルーター側でパケットの分割展開、再分割などの無駄な作業が発生し、ネットワークのスループットが遅くなることがあります。

まとめと付録

通信系路上の一番性能の低いものに合わせて全体の性能が決まってしまうので、見直すポイントも多く、ネットワークが安定して繋がるのってかなりシビアで難しい物なんだなぁと痛感しました。ただ、ピュアオーディオみたいな質の幻想でなくて、光の速さという物理限界がある分幾分マシかなぁ。

最後に、家の中のネットワーク図です

f:id:Dolpen:20160226155738p:plain

青は光回線、緑は 1Gbps でリンク、オレンジは 100Mbps でリンクしています。回線業者はソフトバンクBBで、フレッツ網使ってるので先述の説明と構造が若干違うのですが、それでも IX まで ping が 4ms くらいです。
無線の親機はハブとして動かしていて、無線向けの DHCP や経路制御は RTX1200 側に一任することで、ダブルルータを回避しています。無線親機が GbE 非対応で 11ac もない古いものなので、近いうちにリプレースする気がします。

なんか書く

こんにちはこんにちは、dolpen といいます。どるぺんと読みます。 長らくURLだけ取って放置してたんですが、そんなにたくさんの人に読んでほしいわけでもない微妙な文章を置いていこうと思っています。

なぜ今更なのか

少し前までは自前でブログシステムを作って運用していましたが、その理由の大半が markdown 非対応であるというもので はてなブログが対応している以上自前で作ってもあまり良いこと無いよね、と思い使い始めることにしました。

どうしてブログなのか

議論や考察など、ことばを使う表現活動について

  • 日本語の微妙なニュアンスが英語で表現しづらいように、表現の受け入れられ方はかなりの割合でことばに依存する。
  • 同様に、質を担保するためにある程度の量を要する場合が少なからずある。

という2つの理由から、Twitterなどのミニブログ上で質を追求していくのは困難であると思います。 その他にも、

  • 性質上、なんらかのまとめエントリが作りづらい
  • 後から修正するのが困難である(編集するためにパーマリンクが破棄、変更されるのは好ましくない)

というシステム上の制約もありました。

具体的に何を書くの

多分イカとゆるい技術のことしか書かないと思います。ドキュメンテーションに近い技術エントリは Qiita に、私生活とかそういうのは Twitter 以上の粒度でやると多分破滅が近いので書かないと思います。

https://dolpen.net/ twitter.com github.com qiita.com