Git Product home page Git Product logo

deptorygen's People

Contributors

numanicloud avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

deptorygen's Issues

コレクションに対してもっと簡単に注入したい

コレクションにインスタンスを注入する際の記法は少々煩雑だ。

// Service抽象クラスを継承する ServiceA, ServiceB, ServiceC があるとして
[Factory]
interface IFactory
{
	ServiceA ResolveServiceA();
	ServiceB ResolveServiceB();
	ServiceC ResolveServiceC();

	[Resolution(typeof(ServiceA))]
	[Resolution(typeof(ServiceB))]
	[Resolution(typeof(ServiceC))]
	IEnumerable<Service> ResolveServices();

	Client ResolveClient();
}

複数のクラスをコレクションに注入したいので、ServiceA, ServiceB といったクラスを個別に生成したいわけではないシナリオも多いはず。

以下に示すものくらい簡潔に書きたい。

[Factory]
interface IFactory
{
	[Resolution(typeof(ServiceA))]
	[Resolution(typeof(ServiceB))]
	[Resolution(typeof(ServiceC))]
	Service ResolveServiceC();

	IEnumerable<Service> ResolveServices();

	Client ResolveClient();
}

アナライザーを .NET Frameworkで書く

現状アナライザーがnetstandardで書いてあるので、Visual Studioで動かすためにユーザーがDLLを手動で配置する必要がある。

netframeworkで書けばこの手順が要らなくなるかもしれない。

依存関係解決に使うオブジェクトとして選ばれる優先度をきちんと決める

依存関係を解決するとき、現状では以下の選択肢がある

  • ファクトリーの持つフィールド
  • 解決メソッドの持つパラメータ
  • 別の解決メソッド
  • キャプチャしたファクトリーの持つ解決メソッド
  • ファクトリー自身
  • キャプチャしたファクトリーそのもの

これらのうち、解決に使用できるオブジェクトが複数ある状況下でどれが選ばれるのかが成り行きで決まっているので、きちんと優先順を決めたい。

Resolution属性で指定されている具象型を解決する際にキャプチャもチェックする

以下のように、ある具象クラスを解決するファクトリーIScopedFactoryと、その具象クラスを生成してほしい解決メソッドを持つIFactoryがある。

[Factory]
interface IScopedFactory
{
    EventManager ResolveEventManager();
}

[Factory]
interface IFactory
{
    IScopedFactory Scoped { get; }

    [Resolution(typeof(EventManager))]
    IEventHandler ResolveEventHandler();
    [Resolution(typeof(EventManager))]
    IEventSource ResolveEventSource();
}

現状ではIFactoryは、キャプチャした解決メソッドを使わないように生成される。Resolution属性に指定した型に対してもキャプチャを考慮してほしい。

戻り値の型の異なる複数の解決メソッドで同じインスタンスを返したい

以下のようなシナリオで、戻り値の型の異なる複数の解決メソッドで同じインスタンスを返したいことがある:

[Factory]
interface IFactory
{
    // 1つのクラスが2つのインターフェースを持っていて、
    // それぞれの型についてインスタンスを解決したい
    [Resolution(typeof(EventManager))]
    IEventHandler ResolveEventHandler();
    [Resolution(typeof(EventManager))]
    IEventSource ResolveEventSource();
}

class EventManager : IEventHandler, IEventSource
{
    // 他の場所から来たイベントをプロキシする処理
}

同じResolution属性をつけたメソッドでは同じインスタンスを返すようにもできるが、異なるインスタンスを返すようにしたい場合はどうする?

案1: Transientスコープを活用する?

同じResolution属性をつけたメソッドでは同じインスタンスを返すようにした場合、異なるインスタンスを生成したい場合はキャッシュをしない解決メソッドとして定義する方法がある。

[Factory]
interface IFactory
{
    [Resolution(typeof(EventManager))]
    IEventHandler ResolveEventHandlerAsTransient();
    [Resolution(typeof(EventManager))]
    IEventSource ResolveEventSourceAsTransient();
}

とはいえ、これでは解決メソッドごとにキャッシュを持ちたい場合に役に立たない。

案2: IDで区別

Resolution属性に何らかのIDを持たせると良いかもしれない。

[Factory]
interface IFactory
{
    [Resolution(typeof(EventManager), Id = 0)]
    IEventHandler ResolveEventHandler();
    [Resolution(typeof(EventManager), Id = 1)]
    IEventSource ResolveEventSource();
}

別々のIDであれば、別々のインスタンスが生成され、キャッシュもされる。
Resolution属性ではなく戻り値の型だけでインスタンスが決定される場合もあるので、Resolution属性ではなくてまた別の属性として用意するほうが良いかも?

[Factory]
interface IFactory
{
    [Resolution(typeof(EventManager))]
    [Unique(0)]
    IEventHandler ResolveEventHandler();
    [Resolution(typeof(EventManager))]
    [Unique(1)]
    IEventSource ResolveEventSource();
}

案3: ミックスインでスコープを分ける

同一のインスタンスを返したいとき、他のファクトリーの解決メソッドに委譲する手がある。

[Factory]
interface IScopedFactory
{
    EventManager ResolveEventManager();
}

[Factory]
interface IFactory
{
    IScopedFactory Scoped { get; }

    [Resolution(typeof(EventManager))]
    IEventHandler ResolveEventHandler();
    [Resolution(typeof(EventManager))]
    IEventSource ResolveEventSource();
}

現状、Resolutionで指定されている型がキャプチャしたファクトリーの解決メソッドと一致していても、戻り値の型が一致しているのでなければその解決メソッドに委譲することはない。

仮に委譲するようにしたとして、これだけのためにファクトリーを1つ生成したりするのは直感に反するためあまり嬉しくなさそう。

ところで、Resolutionで指定されている型に対してキャプチャしたファクトリーへ委譲する機能自体は欲しいと思う。

用語集が欲しい

Deptorygenドキュメントで使用する用語をきちんと決めて、それをまとめたドキュメントを書きたい

  • 解決メソッド
  • ファクトリー
  • 解決メソッド定義
  • ファクトリー定義
  • キャプチャ定義
  • キャプチャ
  • ミックスイン
  • 生成/コード生成の違い
  • インスタンス
  • Transient/Cached
  • 寿命
  • プログラマー
  • ユーザー

自分自身を解決するメソッドがあると、コンストラクタ引数に余分なものが現れるのが面倒

次のような定義があるとする。

using Deptorygen.Annotations;

namespace Sample
{
	class TestProvidee
	{
	}

	class TestService
	{
	}

	[Factory]
	interface ITestFactory
	{
		TestProvidee ResolveTestProvidee();
		[Resolution(typeof(TestFactory))]
		ITestFactory ResolveTestFactory();
	}
}

生成されるコードは次のような感じ。(いちどTestFactoryを生成してから、もう一度生成する必要がある)

// <autogenerated />
#nullable enable
using System;
using System.Collections.Generic;

namespace Sample
{
	internal partial class TestFactory : ITestFactory
		, IDisposable
	{
		private TestProvidee? _ResolveTestProvideeCache;
		private TestFactory? _ResolveTestFactoryCache;

		public TestFactory()
		{
		}

		public TestProvidee ResolveTestProvidee()
		{
			return _ResolveTestProvideeCache ??= new TestProvidee();
		}

		public ITestFactory ResolveTestFactory()
		{
			return _ResolveTestFactoryCache ??= new TestFactory();
		}

		public void Dispose()
		{
			_ResolveTestFactoryCache?.Dispose();
		}
	}
}

いま、TestProvideeクラスのコンストラクタでTestServiceを要求するようにする。

class TestProvidee
{
	public TestProvidee(TestService service)
	{		
	}
}

これでコード生成しなおすと、TestFactoryクラスのコンストラクタ引数が変化する。

public TestFactory(TestService testService)
{
	_testService = testService;
}

ここで次に、TestProvideeのコンストラクタがふたたび引数無しに変更されると、再コード生成されたときにTestFactoryクラスのコンストラクタも引数無しになって欲しいが、そうはならない。

なぜなら、TestFactory自身がTestServiceを要求していて、かつTestFactoryTestFactory自身を生成しなければならないから。

// こいつが悪い
public ITestFactory ResolveTestFactory()
{
	return _ResolveTestFactoryCache ??= new TestFactory(_testService);
}

自分自身を生成するためだけに必要としている依存関係は依存関係と見なさないよう修正する必要がある。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.