0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

相互参照するクラス

Posted at

相互参照するクラス

コンストラクタで依存関係を注入する設計にしている場合、相互参照をどのように処理するかは頭が痛い問題です。依存関係を整理できれば一番良いのですが、そうはいかないケースもありますよね。

かといって、外部クラスを直接参照するようなコードを書いて、ユニットテストではSpy等で無理やりモックしてしまうとコードはスパゲッティになっていきます。(今日仕事でそんなコードを見てしまい、勢いでこの記事を書いています。)

LazyIntializerを使った相互参照

LazyIntializerはその名の通り初期化処理を遅延実行するので、相互参照があっても問題なくインスタンスを生成できます。初期化処理はlockによって保護されるので、非同期呼び出しが複数あっても1回だけ実行されます。

インスタンス生成後のアクセスは非同期処理を通らないので高速に実行されます。
TypeScriptでDouble Checked Lockを使う

インタフェイスを注入しておけばテスト時には簡単にモックできますし、簡易的なDI Frameworkとしても利用できます。

import {LazyInitializer} from "ya-syn";

class ServiceA {
    constructor(readonly serviceB: LazyInitializer<ServiceB>) {

    }

    async methodA() {
        console.log("methodA called")
        const serviceB = await this.serviceB.get()
        await serviceB.methodB()
    }
}

class ServiceB {
    constructor(readonly serviceA: LazyInitializer<ServiceA>) {

    }

    async methodB() {
        console.log("methodB called")
    }
}

class Provider {
    readonly serviceA: LazyInitializer<ServiceA> = new LazyInitializer(async () => {
        return new ServiceA(this.serviceB)
    })

    readonly serviceB: LazyInitializer<ServiceB> = new LazyInitializer(async () => {
        return new ServiceB(this.serviceA)
    })
}


async function main() {
    const p = new Provider()
    const serviceA = await p.serviceA.get()
    await serviceA.methodA()
}

main()

実行結果

% npx ts-node src/di.ts
methodA called
methodB called

ya-syn

セマフォがあるとTypeScriptでコードを書くのがグッと楽になります。ぜひ使ってみてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?