【プログラミング】MVC,MVPを理解するためのレイヤーアーキテクチャ

 

少しだけ長いので、根気を持ってお読みください。

 

レイヤーアーキテクチャについて

 

言葉で表現すると

  • 個々のオブジェクトの設計に依存しない、概念的な部分の設計思想
  • 各機能の「置き場所」の定め方
  • あるいは、各機能の依存先を決める手段
  • 責務を明確化する思想

 

この記事を読んでいるということは、MVPやMVCについては聞いたことがある、実践したことがある人は多そう

前提知識としてあると理解しやすい、かも

 

 

詳しく説明してくれている記事がありました

 

qiita.com

 

引用

"レイヤアーキテクチャというのはアプリケーションを責務に応じたいくつかの層としてとらえる設計手法のことです。"

 

表面的にはまさにこの通りですが、層という概念に当てはめてしまうのはいささか語弊があります

 

この記事のMVCの部分はすっ飛ばして(失礼ではありますが)レイヤーアーキテクチャの部分だけを読んでいただけると良いと思います

 

 

今回伝えたいことは、プレゼンテーション層、ドメイン層などで固定化されたレイヤーの話ではなく、必要な責務を考え、自身のアプリケーションに応じて必要なレイヤーを考え出すことをレイヤーアーキテクチャの知識として定義したいという話です

 

 

アーキテクチャの歴史

 

レイヤーアーキテクチャの理解のために、過去のアーキテクチャの歴史をまず振り返ってみます

 

オブジェクトアーキテクチャの歴史

 

いわゆる、個々の機能の設計です、大まかに3つの進化がありました

 

goto statement programming

goto文 - Wikipedia

 

プログラムの要所要所にラベルを定義し、goto文を使用して指定したラベルに飛ぶことで、ロジックを全て縦に記述しながらも強引にモジュール化を図るプログラミング手法です

 

あっちこっちに処理が飛び、人間の脳内キャッシュを膨大に使用するので今見ると非常にスパゲッティコードに見えるでしょう

 

www.linuxdigest.org

 

なぜgoto文が悪であるのかはこちらの記事を読むとわかりやすいと思います

 

 

 structured programming

構造化プログラミング - Wikipedia

 

構造化プログラミングのことです、かなり端折った説明をしますので本来の意味合いを知りたいのであれば書籍などを購入すると良いと思います

 

構造化プログラミングはgoto-lessとも呼ばれ、ロジックを上流(状態)、下流(処理)に分けることで上流の状態に応じた下流の処理を呼び出すという概念が生まれました

 

しかし、処理を分割すればするほど上流の状態が増えることになり、完全なロジックの分離はほとんど不可能でした

 

しかし、goto statementにはなかった、処理の記述に流れを作るという点において、近代のプログラミング思想の根底に関わっています

 

object oriented programming

オブジェクト指向プログラミング - Wikipedia

 

皆大好きオブジェクト指向です、もはや呪いと言っても過言ではなさそうです

 

カプセル化ポリモーフィズムなど様々な概念が存在しますが

 

データとロジックを一つのまとまりとして記述し、処理の関心を内側にのみ向け、処理に応じたメッセージを発行することで、他のオブジェクトの処理のトリガーとして影響を与えつつ、その影響に関心を持たない

 

という概念になるかと思います

 

これを端的に表したものがオブジェクトという言葉であり、またクラスという機能として表現されているものです

 

この概念を用いることで、structured programmingだけではなしえなかったロジックの完全な分離が可能になりました

 

 

オブジェクトアーキテクチャのまとめ

goto statement programmingで処理を記述するという行為を行い

structured programmingで処理の流れの概念ができ

object oriented programmingでロジックの分離を行った

 

 

しかし、これでは処理の置き場所はオブジェクトで定義できましたが、オブジェクトの置き場所が定義できていません

ここからはオブジェクトのまとまりをレイヤーとして定義するアーキテクチャについてです

 

 

多層アーキテクチャ

多層アーキテクチャ - Wikipedia

 

近代的な設計思想は多数ありますが、そこから多層アーキテクチャを抜粋します

 

 

独立したモジュールとして層を定義し、差し替え、機能改善などを容易にすることが可能な設計思想です

 

代表的なものとして三層アーキテクチャが存在します

 

 

三層アーキテクチャは、Webアプリケーションが

  • データベース
  • ミドルウェア(インターフェース)
  • ユーザー(ユーザー操作)

という要素で構成されるようになった際に提唱された概念です

 

 

データ、ロジック、プログラミング的な構成要素としては

の三種類が必要になります

 

データベース

 

データを保持、管理します

 

ビジネスロジック

 

ユーザー操作に応じた処理のロジック、及びデータベースの操作を記述し公開します

 

ユーザーインターフェース

 

ユーザーの操作を検知し、操作に応じた処理をビジネスロジックに問い合わせます

(APIを叩くということと同義です)

 

ビジネスロジックの結果を使用して自身を更新します

 

 

以上のように、各レイヤーに対して明確に責務が割り当てられており

また、依存関係が単方向になっています

 

ユーザーインターフェース -> ビジネスロジック

ビジネスロジック -> データベース

 

データベースは自身以外を知りませんし、ビジネスロジックはデータベース以外知りません

ユーザーインターフェースビジネスロジック以外知りません

 

 

単方向ですね

 

責務を明確化すると、その責務を果たすために必要な情報を持つレイヤーに対してのみ依存する形になるため、結果として関連するロジックを持つオブジェクトの凝集度が上がり、依存関係を簡潔に定義することができます

 

 

レイヤーアーキテクチャのおさらい

 

  • 個々のオブジェクトの設計に依存しない、概念的な部分の設計思想
  • 各機能の「置き場所」の定め方
  • あるいは、各機能の依存先を決める手段
  • 責務を明確化する思想

 

です

重要な部分は責務の明確化です

 

 

MVC,MVPとは一体なんなのか?

 

Model - View - Controller

 

Model - View - Presenter

 

さらにはMVVM,CleanArchitectureまで様々な概念が存在します

 

これらはすべて、レイヤーアーキテクチャに基づいた責務の割り当て方の一つの形です

 

一般的に普及していると思われるMVCの例

 

Modelの責務

  • データを管理する
  • データを更新するためのロジックを公開する
  • (必要であればサーバーと通信する)
  • データの更新を通知する

 

Viewの責務

  • ユーザー操作を提供する
  • Modelの通知、データを元に自身を公開する

 

Controllerの責務

  • ユーザー操作を監視する
  • 操作に対応したModelの更新ロジックを呼ぶ

 

 

こういった形で責務が割り振られています

 

MVPも同じく、責務が割り振られています、MVVMもCleanArchitectureも同じです

 

 

こういった責務を捉える力を持つことで、MVCやMVPなどの既存のレイヤーに縛られない責務の洗い出し、割り振りが可能になります

 

 

例.MVCを、サーバーとの通信部分を踏まえて責務の割り振りを改善する

 

一般的なMVCの責務は前述しましたが、Modelがサーバーと通信する場合、しない場合を踏まえて、サーバーと通信するという責務を切り出し、別のレイヤーとして定義します

 

名前はServiceレイヤーとし、SMVCと定義します

 

 

Serviceの責務

  • Modelを更新するためのロジックを公開する
  • Model更新のためのデータをサーバーから取得する

 

Modelの責務

  • データを管理する
  • データを更新するためのロジックを公開する
  • データ更新の際に必要であればServiceのロジックを呼ぶ
  • データの更新を通知する

 

Viewの責務

  • ユーザー操作を提供する
  • Modelの通知、データを元に自身を公開する

 

Controllerの責務

  • ユーザー操作を監視する
  • 操作に対応したModelの更新ロジックを呼ぶ

 

 

切り出しました

 

MVCにはないメリットとして

このServiceレイヤーはサーバー側にのみ依存し、またローカル用のモックと差し替えることでローカル環境での更新も可能になりました

結果、Modelはシンプルな記述のみになったと思います

 

他にもあるかもしれません 

 

 

こういった形で設計を改善することもできます、参考になったでしょうか

 

 

まとめ

 

責務を洗い出し、責務の一つ一つをオブジェクトの集合体となるレイヤーに割り振ることを

 

レイヤーアーキテクチャ

 

として定義し、広く理解され普及すると良いなと思います

 

 

知識の部分は厳密には間違いがあるかもしれませんが、伝えたいことが伝わっていれば幸いですね

 

【Unity】TestRunnerをbatchmodeで起動した際に謎xmlが吐き出されるバグ【NUnit】

 

 

 

2017/12/22追記

Unity2017.3で治ったようです、よかった

 

 

バージョン

Unity5.6.2f1

 

 

ソース

issuetracker.unity3d.com

 

 

やりたかったこと

Jenkins上でUnityTestRunnerをbatchmodeで起動して、吐き出されたxmlをNUnitPluginで解析

CI環境で自動テストを行いたかった

 

結果

上記Issueのためできなかった

xmlの吐き出される形式がTestSuiteのため、NUnitPluginがうまくパースしてくれない

 

結果、ビルドがコケる

 

Unityさんへ

早く治して・・・

 

【Unity】UniRxで独自オペレータを作る、Observableの拡張

UniRx

github.com

 

いつもお世話になっています。

 

オペレータとは

IObservable<T>の拡張メソッドとして用意されたストリームソースを加工するためのクラス

オペレータはIObservable<T>を継承しているので、メソッドチェーンすることができます

 

ざっくりふわふわとした説明は以上

 

作るもの

オペレータの作成のためにはまず2種類のクラスと拡張メソッドを定義する必要があります

 

具体的には

 

  • OperatorObservableBase<T>を継承したストリームをSubscribeするクラス
  • OperatorObserverBase<TSource,TResult>を継承したストリームを加工する処理を書くクラス
  • IObservable<T>をソースとして受け取り、作成したOperatorObservableBase継承クラスの中にソースを流し込んでnew()して返す拡張メソッド

 

です、最初は何言ってるかわからないと思います

 

適当に作っていきます

 

BossObservableをいうものを作ってみようと思います

原理の説明やこれなんのGenericやねんというのはまったく説明しません 

 

 

オペレータが作成された時(newされた時)に

「デキタヨ!」

オペレータの中を処理が通った時に

「トオッタヨ!」

オペレータの中をErrorが通った時に

「アワワワワワ」

ストリームがCompleteした時に

「ボクニマカセテ!」

 

を出力したいと思います、特に意図はありません。

 

まずはBossObservableを定義します

 

gist.github.com

 

これで定義できました。

基本的にObserverOperatorBaseを継承したクラスの

new()

OnNext()

OnError()

OnComplete()

にいろいろな処理を書くことができます

 

ストリーム中にインプットを止めたい・・・などはかなり多い拡張かと思います

 

 

次は拡張メソッドを定義します

 

gist.github.com

 

 

シンプルです、前回のオペレータをsourceとして引数で引っ張ってきて、newして返すだけです

途中の処理は重複回避やnullチェックですが、重複してもよければ省きましょう

 

以上でBossの定義ができました

 

 

これで全てのストリームに対して.Boss()を挟むことができます

やったぜ

 

 

最後に

UniRxに既に定義されているオペレータだけでも処理はできますが、やはり1つラップされた処理があると使う方は非常に楽です

 

書いていれば理解できてくると思うので、ぜひいろいろ試してみてください

【Unity】Zenjectをマルチシーンで使うときの3つの方法

Zenject関係の過去記事

 

yutakaseda3216.hatenablog.com

 

 

yutakaseda3216.hatenablog.com

 

 

マルチシーンでZenjectを使う

 

少し慣れてきた人向けです

 

Zenjectの中核となるDiContainerは、SceneContextというクラスに紐付いている

 

SceneContextが破棄されることでBindされていたデータも破棄されてしまうため、シーンを跨いでSceneContextが破棄される場合、データを保持できない

 

また、複数シーンを使用する場合、各々にSceneContextが存在する場合、何もしなければContext間でBindされたインスタンスは共有されない

 

そのため、マルチシーン用の機能を使う必要がある

 

方法は三種類ある

 

 

1.ProjectContextを使用する

https://github.com/modesttree/Zenject#global-bindings

 

ProjectContextをSceneContextと同様に作成してprefab化する

InstallerをprefabにアタッチしてAssets/Resources/以下に置くことで

いずれかのSceneContextが読み込まれた際に自動でResources.Loadで生成される

ようになる

 

ProjectContextの特徴として

 

  • DontDestroy属性付きで生成される
  • 全てのSceneContextから参照される

 

ため、各SceneContextから、ProjectContextでBindされたインスタンスを参照することが可能になる

 

また、ProjectContextを操作したい場合はsingletonになっているので

 

ProjectContext.Instance.Container

 

で取得できる

 

どのSceneContextが読み込まれた時でも生成されるので、デバッグでも本番環境でもいてほしいBindインスタンスはこれで良いと思う

 

 

ただ、デメリットとして、いついかなる時でもSceneContextが生成されるとひっついてくるので、Testなどには向かない可能性高し

 

2.ContractNameを使用したParenting

https://github.com/modesttree/Zenject#scene-parenting

 

ProjectContextを使用しない方法その1

 

SceneContextには、ContractNameというパラメータを設定できる

 

これの

 

f:id:yutakaseda3216:20170622174720p:plain

 

ここ

 

f:id:yutakaseda3216:20170622174734p:plain

 

ContractNameを設定することで、SceneContextに属性を設定することができる

 

また勘のいい人はすでにお気づきかもしれないが

 

すぐ下に"ParentContractName"なるパラメータがある

 

ここに指定したContractNameの属性を持つSceneContextがParentとして参照されることで親のインスタンスがBindされているかの様に振る舞うことができるができる

 

Parentに指定したSceneContextが読み込まれていない場合、エラーになるので読み込み順には気をつけよう

 

また、Parentに指定されたSceneContextが先に破棄された場合も同様にエラーになる

 

特徴をまとめる

  • Parentは子のContextを参照できない
  • Parentは子のContextより先に読み込まれていないといけない
  • Parentが子より先に破棄されてはいけない

 

使用するメリットとして、同じContractNameをつけることで

デバッグ

本番用

テスト用

などのContextを用意し、状況に応じてシーンを付け替え、動作を変えることができる点があげられる

 

子からParentになっているSceneContextのContainerを指定してアクセスする場合

子のContainerをInjectで取得して

 

Container.ParentContainer

で取得できる

 

3.ContractNameを使用したDecorating

https://github.com/modesttree/Zenject#scene-decorators

 

ProjectContextを使用しない方法その2

 

前述のSceneParentingよりもシンプル

 

SceneContextを作成するのと同じ様に、DecoratorContextを作成する

 

f:id:yutakaseda3216:20170622175838p:plain

 

DecoratedContractNameに、SceneContextに設定したContractNameを指定する

あとはInstallerを追加して〜で終わり

 

デバッグコマンドの追加や、元となるシーンに対して何かを付加する場合に使用すると良い

元のシーンを全くいじることなく、何かの機能を付与することができる

 

 

またSceneParentingを使用した場合と違って

 

  • DecoratorContextと指定したSceneContextは相互に参照できる
  • DecoratorContextのあるシーンは指定したSceneContextの前にロードされていないといけない

 

点に注意する、特に二個目はクセが強いと思うので・・・

 

 

まとめ

 

SceneParentingだけ使えばいいんじゃないかな感があるものの、それぞれメリットがあるので一考する

 

あと、UniRxとMVP(っぽい)設計とZenjectを組み合わせたアーキテクチャを試行錯誤しながら大体まとめられた気がするので、いつか書きたいなと思う(弱気)

 

Zenjectを使ってUnityの最前線(笑)にいる気持ちで開発しよう!

 

 

ついでに

実はもう一個シーン間の受け渡し方法があるんですが、ZenjectSceneHandlerで調べてください

めんどくさすぎるので紹介しませんでした

【Unity】アセットストアで地雷を踏まないための5つのTips

 

2017/7/10追記

アセットストアがアップデートされ、見た目が大幅に変わりました

一部のTipsがあまり意味をなしていない場合があります

あんまり変わってなかった

 

アセットストア

f:id:yutakaseda3216:20170615120957p:plain

モデル、サウンド、エディタ拡張、完成プロジェクトまで様々なアセットを販売、購入できるプラットフォーム

高品質なアセット(普通に発注すればうん百万は下らなさそうなもの)も100$前後で買えたりします

Unityを使う一つの理由と言っても過言ではないんですが・・・

 

地雷が多いんじゃ

審査さえ通れば誰でも販売できる・・・そのため

  • 品質がまちまち
  • まともに動かすためには特定の手順が必要
  • 他のアセットと競合
  • importするとエラー
  • etc

などなどの問題が多発します

C#やUnityスクリプトなどに深い知識がない人は、エラーを解決できずに泣く泣くアセットを使わない選択をすることもあると思います

 

なので、アセットストアで購入する際に地雷(が比較的多い)を選別するTipsを書こうと思います

 

ちなみに書いてる本人はアセットを15万円分くらい買ってます^p^

 

Tips.1 - 作成バージョンを見る

 

例でPlaymakerを見てみます

このトップ画面の下部にある

f:id:yutakaseda3216:20170615121120p:plain

ここに注目します

f:id:yutakaseda3216:20170615121158p:plain

 

「パッケージはUnity....で作られました」という表示があります

 

ここの部分の対応バージョンが自分の使用しているバージョンをサポートしているかを見てみましょう

この画像では最高で5.4.0となっています

 

 

つまるところ、現在最新版のUnity5.6.1で正常動作する保障はされておりません

表記がそうなっているだけで、5.6での動作を確認している場合もあるようです

 

 

さすがに、Unity5で使用したいのにUnity4で作成されたアセットなどの場合は気をつけましょう

 

Tips.2 - アップデートの頻度を見る

 

アセットは、アセットの開発元がユーザーからのバグ報告対応や、Unityのアップデートに合わせてバージョンアップする場合があります

 

このアップデートの頻度が高いほど、信頼度が高いです

 

f:id:yutakaseda3216:20170615121158p:plain

 

バージョンの数字をクリックするとリリースノートを見ることができます

 

また、サポートウェブサイトとパブリッシャーサイトもチェックしてみましょう

詳細なアップデート履歴が観れる場合があります

 

ここが404NotFoundになっている場合、アップデートはほぼ絶望的と思ってもらって構わないと思います

 

Tips.3 - ディレクトリ構成を見る

 

アセットに含まれるファイルやフォルダ構成をパッケージ内容という部分で見ることができます

f:id:yutakaseda3216:20170615122526p:plain

 

ここの判断基準は幾つかあります

 

  • StandardAssetsが含まれているかどうか

 

 

まず、StandardAssetsが含まれている場合が一番危険です

Unity4やUnity5のStandardAssetsが含まれている場合がある上に、ディレクトリが違う場合重複してインポートされます

 

そのため、DemoSceneなどで使用しているという理由でStandardAssetsが含まれている場合は注意しましょう

 

もちろんimport時にチェックボックスを外し、importしなければ問題はないです

 

 

  • パッケージのルートディレクトリが一つになっているかどうか

 

 

ルートディレクトリが一つになっていない場合(最近はアセットストアのルールで絶対になっているらしい?)

自分で作ったディレクトリと重複する可能性があるため、非常に困ります

 

 

 

 

これもおかしな話です、かなり少数ですがPluginsディレクトリに入っているアセットが幾つかあります

 

追記

 

Arbor2(https://www.assetstore.unity3d.com/jp/#!/content/47081)の作者様よりご指摘がありましたので引用させていただきます

Pluginsに入っていることはアセット提供側からするとメリットがあるため、逆によく考えられていることの方が多いかもしれませんね

 

なので、それ以外の場合(モデルデータや明らかにおかしいもの)は少し考えた方が良さそうです

 

 

 

 

ソースコードの名前がCameraControllerやGameControllerなど、重複する可能性の高い名前の場合注意が必要です

スクリプトを消すことでアセット自体が動作しなくなることもあり、コードを修正する必要が出てきます

 

 

  • ファイルの名前に全角文字やスペース、特殊文字が含まれているかどうか

 

 

これは基本的には問題にならないのですが、Gitなどを使用したバージョン管理をする場合非常に問題になります

一度繁体字が紛れていたことがあり修正に時間を取られました

気をつけましょう

 

 

Tips.4 - レビューではなくサポートフォーラムを見る

 

 

ページ下部にあるレビュー欄

f:id:yutakaseda3216:20170615123840p:plain

 

仮でピックアップしてみましたが、全く当てになりません

現在のバージョンではないレビューの場合があるためです

 

また、1年前や2年前の投稿が出てくることはザラです、信用しないようにしましょう

 

現在進行形でアセットの評価をみたい場合はこちらを利用すると良いです

 

https://forum.unity3d.com/forums/assets-and-asset-store.32/

 

Unityの公式フォーラム内にある、AssetStore関連のスレッド群です

 

利用したいアセット名で検索することで、ユーザーの感想、問題点の共有やデベロッパーとのやりとりを見れる場合があります

 

 

私もデベロッパーに(本当にたまに)意見を言っています、だいたい拾ってくれますし丁寧に答えてくれることが多いです

英語ですが

 

 

Tips.5 - 使った人のレビューブログを見たり、可能なら直接聞く

 

 

当たり前の話ですが、一番確実です。

特にUnityはコミュニティが強いので、日本人もたくさんいます、聞きましょう

 

 

 

最後に

「超個人的」な印象ですがSimpleとかEasyとかついてるアセットをあまり信用してないです

判断基準の一つになれば幸いです、良いUnityライフを

 

 

アセットもバリバリ使ってる現在開発中のゲームの公式サイトはこれ(宣伝)

https://www.studiophantomisland.com/

 

 

よろしくお願いしますーす

BitBucketのプラン改定でLFS容量が10GBから1GBになった話

BitBucketのプラン改定

f:id:yutakaseda3216:20170614141506p:plain

 

Pricing Updateというまあありていにいえば価格改定が行われ

その結果無料プラン、有料プランにLFSの容量が組み込まれた

 

改定前

リポジトリ容量2GB limit

LFS容量10GB limit

 

改定後

リポジトリ容量2GB limit

LFS容量1GB limit  

(!!!!?????)

 

LFSの容量は有料プランの段階に応じて5GB -> 10GBと増え

それ以上はストレージ容量を月額で買う感じのようだ

 

値段は10$ / monthで100GB使える

 

結局

むしろよく10GBも使わせてくれてたよなと思いつつも、急に変えられて困った

支払い関係でpaypalが使えず(外部連携すれば使える)

JCBのカードしか持っていない弱小の僕はgithubの有料プランへ移行した

 

privateリポジトリを作るためにgithubの有料アカウントを取得

毎月5$払ってLFS容量を50GB買っている

 

だいたい月に12$弱持ってかれてる計算だ

 

Githubに移行してから

とりあえずリポジトリで使用しているLFS容量が9GBほどあったので、cloneに多大な時間がかかる

また、使用できるリポジトリの通信帯域制限50GBがあるのでcloneコケすると軽く数GBの帯域を持って行かれた

 

通信帯域制限はBitBucketにはなかったと思うので、Github有料にする前によく考えればよかったなーと思う

 

Nowでこんな感じ

f:id:yutakaseda3216:20170614142647p:plain

 

 

兎にも角にも、サービスを使っているとこういうコストが生まれるので

やっぱり自前でサーバー立ててgitlabとか使った方が最終的には安く済みそうだなと思った。

 

終わり

【Unity】ZenjectのAsSingle(),AsCached(),AsTransient()の違い

 

 

調べてみた。

 

超適当にテストコード書いて挙動を確かめた

メソッドの名前が意味不明なのは許してほしい

 

gist.github.com

 

挙動を理解しないとヤバい事故生みそうだなこれという結果になった

結果的にはAsSingle()でInjectされてる参照がUnbindで破棄されてなくて、Inject済みの参照をもう一回Bindし直してた

あとGCで回収されてなかった

 

そもそも

As〜ってなんぞやというと

BindするクラスやインスタンスのBind方法の定義である

 

https://github.com/modesttree/Zenject#binding

 

AsSingle(),AsCached()はキャッシュするのね〜

AsTransientはキャッシュ作らないのね〜くらいの認識だと

しぬ(しんだ)

 

AsTransient()

クラスをBindする際に複数インスタンスの生成を許容する

クラスがContainerにBindされている際、参照情報が一つであればResolveで解決出来る

 

しかし、Resolveは単一のインスタンスに対してのみ参照を解決するため

AsTransientでBindを複数回行っていた場合、multiple instanceエラーを吐く

 

gist.github.com

 

AsTransientでのBindには必ず.WithId()を使用してIdentiferを定義するか

ResolveAllを使用した参照解決を図るようにした方が良い

 

また、As〜などの定義をしなかった場合はデフォルトでAsTransientになる

Factoryなどを使用したインスタンス生成もAsTransientになるため、Instantiateで複数生成したクラスに対してResolveするとエラー吐く、気をつけよう

(まあInstantiateした時点でResolveすることなんてほとんどないんだけど)

 

AsCached()

Bindされているクラスがすでにインスタンス化されている場合、同じインスタンスを使い回すようにする

また、UnbindするとCacheされていたインスタンスは全て参照を破棄される

 

同じインスタンスは使いまわしたいが、Unbindで破棄したい時はこれを使用する

クセがなく、取り回しがしやすいので動的Bindをする際は非常におすすめしたい

 

AsSingle()

クラスをBindする際にSingletonとして定義する

クラスがContainerにBindされている際は

「そのContainer内で唯一存在するクラス」となることが保障される

 

Bindされている際は、Cachedと同じ挙動をする

 

しかし、Unbindを呼んだ際に

Bindした自身の参照を他のクラスにInjectしなくなるが、生成したインスタンス自体は破棄されずキャッシュされたまま

のため、Unbindを呼び、その後もう一度同じクラスをBindしても

new()などが走らず、キャッシュされていた参照をもう一度Bindして使い回す

 

gist.github.com

(HashCodeでやっているが、他にもResolveで返ってきた値の中身を書き換えたところどちらも書き換わった)

 

AsSingle()は必ず一つ、かつ一度しか生成されないオブジェクトに対してのみ使用しよう

 

 

まとめ

readmeには、「AsSingle()をほとんどの場面で使いたいと思うでしょうが〜」とか書いてあるけど

基本AsCached()でいいわ。

 

AsSingle()で動的Bindすると初期化走らせられなくてビビった結果、動作を知れたのでまあよかったと思う。

 

あと、ZenjectのUnitTestマジで神以外の何物でもないのでバンバン使っていきたい