「七賢 純米大吟醸 田舎酒屋の酒」を飲んだ
友人から頂いた七賢(ほんとありがとう🙇)!!今回は純米大吟醸の火入れ。
精米歩合37%まで磨かれた贅沢な一本です。酒米は「夢山水(ゆめさんすい)」。この酒米のお酒は初めてかも。
飲んだ第一印象は「若い!」でした。火入れとは思えないフレッシュさ、37%磨いたとは思えない豊かすぎる青りんご風味のコクがあります。
ちょっと酔った状態で「これは生酒です」と言われて差し出されたら、うっかり信じてしまうかもしれません。
このお酒の特徴としては、辛味と甘味の絡み方でしょうか。
これまで飲んできた七賢の生酒とは違い、辛味が常に感じられ、最後に生き残るのも辛味なのですが、この辛味が甘さと美味いこと交じり合って、より一層の旨味を引き立てています。
スイカに塩をかけると甘さがますというのがあるじゃないですか。ああいう感じです。
キリッと辛いわけでは無い、絶妙な辛さによって、このような味わいを出すことに成功しているのだと思われます。
このお酒、全体的な味はかなり派手で、(変な表現ですが)まるで恐れを知らない高校生のようにエネルギッシュなのですが、開封後一週間ぐらいすると全体的にかなりまろやかになり、安心感のある味わいになります。まるで高校生が社会人10年目ぐらいになったかのよう。僕は最近は派手な酒でない方に好みが傾いてきているので、後者のほうが好みではあります。
純米大吟醸でしっかりと旨味を感じれる酒が飲みたい!という方には間違いなくおすすめの一本です。スイスイ飲むのではなく、夜、静かなところでじわじわ飲みたいお酒です。
やはり七賢は美味い!!ごちそうさまでした。
クーパーで長野〜群馬〜山梨〜静岡を走った
2泊3日で走ってきました。今回は関東に住む友人のクーパーSとツーリング。
1日目
とりあえず集合場所は諏訪湖付近とし、僕は早朝に出発。名神と中央道使ってひたすら長野を目指しました。
Starbucksあるサービスエリアで休憩している写真。ボンネットの上にコーヒー乗ってます。
渋滞も事故もなくスムーズに合流ポイントに向かうことが出来ました。
合流してからは県道40号を利用して一気に霧ヶ峰方面を目指します。
地図で見ると狭そうですが、意外と走りやすくおすすめです。グニャグニャとしてるのでドライビングが楽しい。
一気に駆け上がって..
高原に入って「霧の駅」に到着。
霧の駅周辺
ギリギリ晴れてる!!
二台並べてパシャリ。どちらもF56クーパーSです。ただし僕のはATで友人のはMT。ドライビングを楽しむならやはりMTですね。
まさに高原といった風景。
なんか飛んでます。風立ちぬっぽいです。
馬もいます。
少し散歩した後、ここから美ヶ原の方へ向かいます。
とにかく高原を走りまくる。
三峰大展望台
美ヶ原方面へ向かう途中にあった展望台です。駐車場が少なくタイミングによっては入れないかもしれませんが、その分景色はかなり良いです。
小さな丘に登って当たりを見渡せるようになっています。
京都にもほしいなあこういうとこ。
ここから上田の方に向かいます。
2日目
7時ぐらいに目が冷めて、直ちに朝食を済ませ、一年前は雨で何も見えず悔しい思いをした鬼押ハイウェイへ向かいます。
早朝のワインディング。朝のワインディングは本当に気持ちがよくて素晴らしい。窓も開けて気分爽快です。
道の駅で休憩。
この後、駐車場を出ようとするといきなり車内から
「ビィイィィイィイイ!!」
という謎の音が!!なんだこれ!?と焦りつつ車内や車外を見渡してもなにも異常は見られず。すぐ鳴り止んだのでしょうがなくそのまま走り出しました。
数分後、メーターの上にキリギリスが現れました。音の犯人はこいつなのでした。
3日目
この日は基本的には高速を走って帰るのみですが、ちょっとだけ富士山を見ておくことにしました。
本栖湖
ここから富士山の裏側が見えます。平日なので誰もいません。
雄大!!
雲がかかってるが見えるぞ!
思わずクーパーと記念撮影。
ここからもうちょっと行ったところにも撮影できるスポットがあって、
窓から富士山を眺めることができました。
この後は新東名と新名神を駆使しして家に帰りました。三日間で1200kmぐらい走ったかと思います。やはり長野は面白い。高原が気持ち良すぎる。関西には高原が殆どないので羨ましい限りです。
次は何処に行こうかなあ。
※ 去年の涙をのんだ雨天ツーリングの様子はこちらをご覧ください。
funnelbit.hatenablog.com
funnelbit.hatenablog.com
クーパーで「京都美山 かやぶきの里」にいった
今日こそは家でのんびりしょうと思っていたのですが、外が晴れてきてどうしても我慢できなくなり、プチドライブに行ってきました。
目指したのは京都南丹にある「かやぶきの里」。ざっくりいうと白川郷の京都版、ということになるのでしょうか。
市内を抜けると時間がかかりすぎるので、京都縦貫道を園部まで北上し、そこから県道19号→県道38号を使ってアクセスします。
場所は「道の駅 美山ふれあい広場」からかなり近いです。京都のバイク乗りであれば説明不要ですよね。
かやぶきの里直ぐ側に駐車場があってクーパーはそこに停めてあります。
青い空!!本日梅雨明けが発表されましたね。嬉しい限り。
やって来ましたかやぶきの里。
広がる田んぼと山々。
集落には入る事ができます。記念資料館になっている茅葺き屋根の建物もあります。
日当たりの良い屋根には苔がたくさん生えてますね。
夏のこういう道、良いですよね。共感してくれる人いるかな。
何故か気になった、朽ち果てようとしている消火栓の標識。
屋根の上は新たな命の礎となっています。
こういうところには十中八九、美しい川がある印象です。入って遊ぶこともできるようですね。
綺麗すぎる!!
自然と共存している様が美しいですね。
帰りはちょっと美山ふれあい広場によってみたのですが人がいっぱい。
多すぎて隅っこの方に止める。
ソフトクリーム食べたかったのですが長蛇の列ができていたので諦めました。いそいそと帰路について、京都縦貫道を使って一気に帰宅しました。
この日は大体200kmぐらい。高速使えば楽ちんですね。
今年の夏は色々行きたい!次はどこに行こうかな。
電車で丹波篠山に行った
ほとんど地酒を目的として丹波篠山に行ってきました。お酒を呑むということで電車です。
京都の下のほうから出発ですので大阪まで出た後、篠山口行き(終点です)に乗って到達しました。
王地山稲荷神社
またの名をまけきらい神社というこの神社は、かつてどうしても他藩に相撲で勝てない篠山藩のために、稲荷の神が篠山藩のために力士に化け、勝利したということから「勝利の神」とされているようです。
稲荷はその性質から様々な「利益をもたらしてくれる神」となり易いのですが、勝利の神というのはなかなかに珍しいのではないでしょうか。
なかなかの数の鳥居が奉納されています。
おお〜おもしろい!
すぐに本殿に到達しました。なかなか立派。
切り株の上に社が建てられていて面白い。
河原町妻入商家群
かつての城下町の町並みを残した通りになります。今も人が住まれている建物が多いですね。
大正感ある電灯がちょっと不思議かも。
篠山城大書院
篠山城内にある巨大な木造建築物です。かつては藩の公式行事などに使用されたのだとか。
ちなみにこの建造物は一度火事にあって消失しているため、現在あるものは復元されたものです。
内部は見学可能です(400円)。
肝心の篠山城ですが、今は取り壊され、間取りごとに区分けされた広場が残るのみとなっています。
このお城、譜代大名(徳川方の大名)のお城で一国一城令でも取り壊されもせずにいたのですが、明治になって城が国のものとなり売り飛ばされた結果、取り壊され、今の有様になってしまったようです。
丹波栗菓匠 大福堂
和菓子屋さんです。1階が販売店、二階にほんのすこしですが茶席が設けられています。
黒豆わらび餅を注文してみました。すこしコリコリ感というか、硬めの食感で面白い。あとこの黒豆、めちゃくちゃうまいです。この豆だけほしいくらい。
ほろ酔い城下蔵
鳳鳴酒造の蔵設備見学施設です。出入り口は小売店になっていて、お酒の販売と試飲がなされています。
ちなみにこの建物は重要文化財です。
これまで酒造りで使われていた道具が並んでいます。
いい雰囲気ですね。
更に奥へと続いています。どこからかクラシック音楽が聞こえてきます。
酒樽が並んでいます。
鳳鳴酒造のお酒がズラリ。
おお!!麹室!!!
中は天井が低く暑いです。今は照明がありますが、本来なら光も一切ない空間です。
30度はすごいですね...このもやしをふりかける工程は非常に重要で、杜氏にしかできないはずです。
これ、スピーカー付いてますよね。これは一体?
おわかりでしょうか。つまり音楽を聴かせることで、酒の質が変わるというのです(マジか!?)。さっきから鳴っていたクラシック音楽はこれなのでした。
出入り口では試飲コーナがあったのでありがたくいただきました。
無料試飲と有料試飲があります。無料の方を頂いた後に有料試飲を行いました。吟醸セットで税込み518円だったかと思います。
全体的にコクが控えめで最後は辛味でキレるお酒です。飛騨高山の酒質とそこそこ似てます。山車をもうちょい飲みやすくした感じですかね。関心したのが有料試飲で飲んだ大吟醸の味わいで、後味がまるで消えるように無くなっていくお酒でした。ほんのちょっぴり辛味を感じれますが、ほとんど魔法のように消えていく。これは面白いなあと思いました。
あともう一つ面白かったのが例の「音楽振動醸造装置」で作られた酒。本当にこんなので味変わるのかよ、植物に話しかけたら伸びるみたいな奴じゃないんかいな、と半信半疑だったのですが、実際に効果を確認するために片方が音楽を聴かせている方、もう片方が聴かせてない方で試飲させていただきました。すると本当に味が違ってて、音楽有りの方はなしの方に比べると角が取れたようにまろやかで、すこしコクがあるようにも感じました。よもやこんな酒造りの方法があるとは...。
この製法で一番美味しくなる曲は何なんでしょうね?気になります。
春日神社
入り口は小さめですが中は結構広い神社。本殿は小さな山を登ったところにあります。
ところでこの神社にあった黒い神馬の絵なのですが、この絵すごいなと。ちょっと写真ではわかりにくいかもしれませんがぜひ拡大して見てください。
まるで見ているものが吸い込まれてしまうようで、それでいて浮き出ているようにも見えるような黒さと、生きているような目、そしてシンプルながらも躍動感を感じさせる構図が凄まじい「生き物」感をかもし出していて、まるで絵から出てきても納得してしまうような、ゾクリとした畏れのような感覚を覚えます。
実際、この絵から黒神馬が飛び出して悪さをしてきたため金網を張ったという伝説も残っているということで、多くの人が同じように感じられてたのかと思います。
本当に凄い絵だと思います。野外展示のために痛みが結構激しそうですが、大丈夫なのかな。
ちなみに出てきて何してたかって言うと畑の豆食い荒らしていたそうです。どんだけ豆食いたいんや。
丹波篠山 郷土料理 懐
真夏ですがエアコンめっちゃ効いてたのでぼたん鍋を食べることにしました。
イノシシ肉はしっかりと火を通さなければなりませんので、強火で色が完全に変わるまで待ちます。
この鍋のイノシシ肉は獣臭さが全く無く、とても食べやすいです。あと味噌も良く出来てて濃いすぎずしょっぱすぎずに調度良い。無限に御飯食べれるような味わいでした。
丹波篠山はド派手な観光地ではなく、観光もできる田舎という感じです。とてものんびりとしており、都会の喧騒をはなれてどこか行きたいなあという人に向いてるのではと思います。多分アクセスは車のほうが電車よりも良いです。電車だと駅から更にバス(本数はあまりない)ですし、電車も三田を過ぎた当たりから各駅に止まり始めるので、高速使って車で来たほうが早いのではと思います。
さて次はどこに行こうかな。
「雪の茅舎 純米吟醸 美酒の設計 生酒」を飲んだ
酒屋でなんとなしに冷蔵庫を見てたら発見したこのお酒。
なんといっても目を引くのが「美酒の設計」という誇らしげな文字。「秘蔵」とか「幻」とか、そういう謳い文句はスルーする事が多いのですが、そんな安直なネーミングではないことと、なによりも雪の茅舎という有名銘柄がそんな文言を載せているものだから、これは試してみなければと思い、すぐさま購入してしまいました。
このお酒には特等の山田錦が使用されており、誇らしげに掲げられています。肉にもA5だとかランクがありますが山田錦にもそんなものがあり、これは上から2番目の品質を誇る山田錦ですね。
いたずらに50%まで削って大吟醸としてないあたりも、このお酒のこだわりを感じます。
さて、まず香りですがいわゆる最近流行りの吟醸香がしっかりしていますが、少し抑えめで美しい香りがします。大人しく、クリアな印象です。
飲んでみると香りから予想した通りの吟醸酒の味をまず感じますが、ほのかな青リンゴのような旨味が少し控えめに感じます。そしてしばし待つとピリッとした辛味を少し感じて辛味が顔を出し、キレていきます。この辛味はその後も結構舌の上に残り続けますね。
この控えめさとちょっとしつこめの辛さが「あれ?どんな味だったっけ?」を引き起こし、その度に一杯、また一杯と杯が進んでしまいます。
このお酒はいわゆる優等生な性質ですがそれにとどまらず、更にその先、品の良さを併せ持っており、これがこのお酒をワンランク上に感じさせる大きな特徴となっています。
さて、せっかくの宅飲みなので燗酒も試します。燗酒にするとはやりの味わい、特に青リンゴのような風味は息を潜めますが、上品さは継続してあります。辛味がほとんど無くなり、米の旨味がとろみのような味わいを見せるようになり、非常に上品な燗酒となります。ちょいと高めの温度でも大丈夫。控えめな性質は継続してあるので、グイッと飲むのが旨味をつかむコツです。
この燗酒、品が良く、後味がスッと溶けるように消えていく。これはかなりすごい。こんなに品のいい燗酒はこれまで飲んだことがないですね。まあ正直、このお酒を燗酒にしたいという人は少ないでしょう。蔵元ですらやめてくれと言うかも知れません。しかし僕はこのお酒の燗には大きなポテンシャルがあると感じます。
誤解されないように申し上げておくと全部燗にして飲みたいわけではないです。キリッと冷やしても、燗にしてもどちらもうまい。どちらの温度にしてもしっかりと設計された「美酒」の根底は揺らがない。そんな懐の深い酒だなあと感じました。
ごちそうさまでした。
Dagger2のProducers 基本的な使い方
square 社製の Dagger を fork して生まれた Dagger2(Google Dagger) は、リリース当初は導入事例があまり見られず手探りな状態が続いていましたが、最近ではプロダクトへの導入事例を聞くことも珍しくなくなりました。僕が関わっているいくつかのプロダクトでも Dagger2 を積極的に利用しており、もはやなくてはならない存在になったと言っても過言ではありません。
ところで、 Dagger2 は形式に沿った Module と Component のコードを書くことで、コンパイル時に依存注入するためのコードを生成し、アプリケーションが依存注入を要求した時に処理を実行するものです。この処理は同期的に行われますが、もしこの処理自体を非同期にしたい場合はどうすればよいのでしょうか。例えば、APIを叩くなどの処理が絡んでしまい、同期的に依存を解決できない場合。また依存解決に必要な処理をどうしてもメインスレッドで待つことが出来ない場合や、パフォーマンスの関係上非同期のほうが都合が良い、などといった場面の場合。我々エンジニアは、どのような書き方をすべきなのでしょうか。
その答えは既に Dagger2 に用意されています。Producers という仕組みを利用するのです。
目次
はじめに
Dagger2 には Producers という非同期で依存を解決する仕組みがあります。
通常の Inject とは違い、@Inject
アノテーションを使うことはありません。
Component は ListenableFuture<T>
を返すメソッドを定義するようにし、 <T>
を注入して欲しいクラスはそのメソッドを呼び出します。そして非同期にインスタンスを受け取るのです。
基本的な使い方
// dagger2 compile 'com.google.dagger:dagger:2.5' compile 'com.google.dagger:dagger-producers:2.5' // これが必要 apt 'com.google.dagger:dagger-compiler:2.5' androidTestApt 'com.google.dagger:dagger-compiler:2.5' provided 'javax.annotation:jsr250-api:1.0'
Producers は Dagger2 を拡張したものですので、追加の dagger-producers
を読み込んでおく必要があります。
簡単な例です。 String を返す Module を考えてみます。記述のポイントは以下のとおりです。
@ProducerModule
を使うことになります。userName(UserData userData)
が注入するインスタンスを返すメソッドです。provideUserData()
はUserData
を生成して返しますが、そのまま返すのではなく、ListenableFuture<UserData>
を返すようにします。
@ProducerModule final class AppProducerModule { @Produces ListenableFuture<UserData> provideUserData() { // ~ なんかめっちゃ時間がかかる処理 ~ return Futures.immediateFuture(new UserData("userName")); } @Produces String userName(UserData userData) { return userData.name; } }
実は Module がもう一つ必要で、それが以下に示す Executor
を返す Module です。
@Module public class ExecutorModule { @Provides @Production static Executor executor() { return Executors.newCachedThreadPool(); } }
この Executor は、非同期で依存を解決する Executor を提供するものです。
次に Component ですが、 @ProductionComponent
を使います。
@ProductionComponent(modules = {ExecutorModule.class, AppProducerModule.class}) public interface AppProducerComponent { ListenableFuture<String> userName(); }
ExecutorModule.class
はここで指定しています。
返り値が ListenableFuture<UserData>
となっている点に注意してください。
注入して欲しいクラス、例えば Activity は、この Component のメソッドからインスタンスを受け取ります。
public class MainActivity extends AppCompatActivity { ... ListenableFuture<String> userDataListenableFuture = DaggerAppProducerComponent.create().userName(); Futures.addCallback(userDataListenableFuture, new FutureCallback<String>() { @Override public void onSuccess(String result) { Log.e("name", result); } @Override public void onFailure(Throwable t) { } }); ... }
これを実行した時、 AppProducerModule
では順番にメソッドが呼ばれ、依存解決を試みます。
@ProducerModule final class AppProducerModule { @Produces ListenableFuture<UserData> provideUserData() { // 1. まずはじめにここが呼ばれる。 return Futures.immediateFuture(new UserData("userName")); } @Produces String userName(UserData userData) { // 1 で解決されるインスタンスが必要 // 2. 1が終わったらここが呼ばれる。 return userData.name; } }
Module は includes
を使うことによって、他の Module と関係をもつことも出来ます。
@ProducerModule(includes = UserDataRequestModule.class) final class AppProducerModule { @Produces ListenableFuture<UserData> provideUserData(APIClient client) { return Futures.immediateFuture(client.get())); } @Produces String userName(UserData userData) { return userData.name; } }
UserDataRequestModule.class
が APIClient
をくれる、というイメージです。
生成されたコードでExecutorはどう使われるか
処理はすべて非同期で、 Executor
を用いて行われます。先ほど ExecutorModule
クラスで Executor
を返すメソッドを定義しましたが、この Executor
は一体どのように扱われているのでしょうか。
生成されたコードを見てみると、その答えがわかります。
public final class DaggerAppComponent implements AppComponent { private Provider<Executor> executorProvider; ... this.executorProvider = DoubleCheck.provider( AppComponent_ProductionExecutorModule_ExecutorFactory.create( ExecutorModule_ExecutorFactory.create())); ...
Executor
は Dagger によって生成されたコードの中で、 Provider<Executor>
として保持されます。
この Provider はそれぞれの Factory クラス内でコンストラクタ経由で渡され、
public final class DaggerAppComponent implements AppComponent { ... this.getUserDataProducer = new AppProducerModule_GetUserDataFactory( builder.appProducerModule, executorProvider, monitorProvider); ...
Factory クラスが compute()
メソッドによって処理を始めた時、 Futures の transformAsync()
メソッドで非同期処理を行うときに get()
メソッドを呼ぶことで Executor
を得ています。
public final class AppProducerModule_GetUserDataFactory extends AbstractProducer<UserData> { ... @Override protected ListenableFuture<UserData> compute(final ProducerMonitor monitor) { return Futures.transformAsync( Futures.<Void>immediateFuture(null), new AsyncFunction<Void, UserData>() { @Override public ListenableFuture<UserData> apply(Void ignoredVoidArg) { monitor.methodStarting(); try { return AppProducerModule_GetUserDataFactory.this.module.getUserData(); } finally { monitor.methodFinished(); } } }, executorProvider.get()); }
補助的な機能
Dagger2には、様々な非同期の依存解決を実現する機能が用意されています。
Producer
先程も登場した Provider<T>
と似た仕組みが Producers には用意されています。それが Producer<T>
です。
まずは Module ですが、いわゆる出口であるメソッド以外で特に変わることはありません。
@Produces @Normal ListenableFuture<UserData> provideNUserDate() { return Futures.immediateFuture(new UserData("normal")); } @Produces @Special ListenableFuture<UserData> provideSUserDate() { return Futures.immediateFuture(new UserData("special")); } @Produces ListenableFuture<UserData> provideUserData(@Normal Producer<UserData> nProducer, @Special Producer<UserData> sProducer) { return sProducer.get(); }
受け取り側は、インスタンスそのものを受け取る or Producer を受け取るかを決定する権利を持っています。この点は Provider<T>
の性質と全く同じです。
これによって provideUserData(...)
は、何かしらの処理によって依存解決処理そのものを決定できる権利を持つのです。
例えば、 Dagger2 のドキュメントの例には Flag を見て処理を分けています。
Component も、そのメソッドを使う注入先の記述も何も変わりません。手を入れるのは Module だけです。
Produced
以下の Module のコードを見てください。確実に例外が発生するコードです。
@ProducerModule final class AppProducerModule { @Produces ListenableFuture<UserData> getUserData() { // ~ なんかめっちゃ時間がかかる処理 ~ throw new IllegalStateException(); // わざと落とす //return Futures.immediateFuture(new UserData("userName")); } @Produces String userName(UserData userData) { return userData.name; } }
この時、Dagger は依存の解決を諦め、スキップを行います。これは Module 内で何か都合の悪い予期せぬ自体が起きた時、例外が起きてクラッシュするのを防ぐためであると思われます。
ただ、例外を自らの手でハンドリングしたいことも有ります。その場合は Produced<T>
を使うことで実現できます。
@ProducerModule final class AppProducerModule { @Produces ListenableFuture<UserData> getUserData() { // ~ なんかめっちゃ時間がかかる処理 ~ throw new IllegalStateException(); // わざと落とす //return Futures.immediateFuture(new UserData("userName")); } @Produces String userName(Produced<UserData> userData) { try { return userData.get().name; } catch (ExecutionException e) { e.printStackTrace(); return "何かが起きた"; } } }
スタックトレースが吐かれ、"何かが起きた"が返されます。
@IntoSet
複数のインスタンスを Set<T>
に保持し、依存を解決する仕組みがあります。それが @IntoSet
です。
Module のメソッドに付与することで機能します。以下の例を御覧ください。
public class AppProducerMultiBindingsModule { @Produces @IntoSet ListenableFuture<UserData> provideNUserDate() { return Futures.immediateFuture(new UserData("normal")); } @Produces @IntoSet ListenableFuture<UserData> provideSUserDate() { return Futures.immediateFuture(new UserData("special")); } @Produces @IntoSet UserData provideDogUserDate() { return new UserData("dog"); } }
Component はこのようにします。
@ProductionComponent(modules = {ExecutorModule.class, AppProducerMultiBindingsModule.class}) public interface AppProducerMultiBindingsComponent { ListenableFuture<Set<UserData>> getSet(); }
getSet()
が呼ばれた時、関連するすべての依存の解決が試みられます。例えば上の Module の例では、すべての provide~ メソッドが、getSet()
された瞬間に呼ばれるということです。インスタンスが生成され次第、Set<t>
の中に追加されていきます。
ListenableFuture<Set<UserData>> set = ((App) getApplication()).getAppProducerMultiBindingsComponent().getSet(); Futures.addCallback(set, new FutureCallback<Set<UserData>>() { @Override public void onSuccess(Set<UserData> result) { Log.e("success", String.valueOf(result.size())); // 3 } @Override public void onFailure(Throwable t) { Log.e("error", t.toString()); } });
すべての処理が終わり次第、 onSuccess()
が呼ばれる、ということになります。
Module 内でこの Set<T>
を利用して新たな値を返す技もあります。以下の例では(意味不明ですが)すべての UserData
の名前を合体させて、新しい UserData
を返しています。
@ProducerModule public class AppProducerMultiBindingsModule { @Produces @IntoSet ListenableFuture<UserData> provideNUserDate() { return Futures.immediateFuture(new UserData("normal")); } @Produces @IntoSet ListenableFuture<UserData> provideSUserDate() { return Futures.immediateFuture(new UserData("special")); } @Produces @IntoSet UserData provideDUserDate() { return new UserData("dog"); } @Produces UserData collect(Set<UserData> data) { Iterator<UserData> i = data.iterator(); UserData normal = i.next(); UserData special = i.next(); UserData dog = i.next(); return new UserData(normal.name + " and " + special.name + " and " + dog.name); } }
@ElementsIntoSet
Set<T>
は Dagger で生成されたコードによって作られますが、 @ElementsIntoSet
を使えば、複数の値を Set<T>
に入れておくメソッドを自分で書くことも出来ます。
@ProducerModule public class AppProducerMultiBindingsModule { @Produces @IntoSet ListenableFuture<UserData> provideNUserDate() { return Futures.immediateFuture(new UserData("normal")); } @Produces @IntoSet ListenableFuture<UserData> provideSUserDate() { return Futures.immediateFuture(new UserData("special")); } @Produces @IntoSet UserData provideDUserDate() { return new UserData("dog"); } @Produces @ElementsIntoSet ListenableFuture<Set<UserData>> provideSet() { return Futures.<Set<UserData>>immediateFuture(ImmutableSet.of(new UserData("foo1"))); } @Produces UserData collect(Set<UserData> data) { Iterator<UserData> i = data.iterator(); UserData normal = i.next(); UserData special = i.next(); UserData dog = i.next(); UserData foo1 = i.next(); // @ElementsIntoSet return new UserData(normal.name + " and " + special.name + " and " + dog.name); } }
@IntoMap
Set<T>
だけでなく Map<K, V>
で扱うことも出来ます。
@MapKey @interface Type { String value(); } @ProducerModule public class AppModule { @Produces @IntoMap @Type("cat") static ListenableFuture<UserData> cat() { return Futures.immediateFuture(new UserData("cat")); // このコードでは呼ばれない } @Produces @IntoMap @Type("dog") static ListenableFuture<UserData> dog() { return Futures.immediateFuture(new UserData("dog")); // このコードでは呼ばれない } @Produces ListenableFuture<Integer> size(Map<String, Producer<UserData>> map) { return Futures.immediateFuture(map.size()); // 2 } }
この例はすべての UserData の合計値を出しているものですが、UserData そのものを受け取っていません。受け取っているのは Producer<UserData>
であり、単に map の size を得ているだけです。そのため、 cat()
メソッドと dog()
メソッドを動かすこと無くサイズを得ています。
まとめ
Producers を使えば、記事冒頭に書いたような要件をすべてクリアできるはずです。ちなみに Module 内のメソッドが一つ一つ非同期に流れていくさまはすこし RxJava ようではありますが、この仕組みはあくまでも非同期な依存注入を実現するためのものです。 RxJava と競合する仕組みでは勿論ありませんし、むしろ組み合わせて使うことでより利便性が向上するものであると理解しています。
効果的に使うことで大きな利益をもたらすと思われますが、まだ実際にプロダクトに導入もしておらず、僕自身も未だ深くは理解できていません。
もう少し遊んでみて、確実に効果の出せる箇所がありそうなら使っていきたいなと思っています。
より深い理解を望む場合は、Dagger2 の公式ドキュメントを御覧ください。 google.github.io