【DryIOC】装饰器

DryIoc中的装饰器一般代表一个装饰器模式。但是结合其他DryIoc功能,尤其是与FactoryMethod结合使用,该概念可能会进一步扩展以涵盖更多场景。
DryIoc 装饰器支持:

  • 常用服务装饰,为服务添加相关功能
  • 根据条件应用装饰器
  • 嵌套装饰器并指定自定义嵌套顺序
  • 泛型装饰器支持:泛型变化,约束,泛型工厂方法
  • 装饰器可以有与被装饰服务不同的重用方式。这里有个额外的为装饰器定义的选项:useDecorateeReuse
  • 装饰器可以装饰封装在FuncLazy及其他包装器中的服务
  • 联合装饰器的重用方式与工厂方法注册,装饰器可以提供额外的型外
  • 使用工厂方式和装饰器条件,可以注册泛型服务的装饰器

1. 常规使用

void Main()
{
	var container = new Container();

	container.Register<IHandler, FooHandler>();
	container.Register<ILogger, InMemoryLogger>();
	container.Register<IHandler, LoggingHandlerDecorator>(setup: Setup.Decorator);

	// resolved handler will be a decorator instance
	var handler = container.Resolve<IHandler>();
	Console.WriteLine(handler is LoggingHandlerDecorator);

	handler.Handle();
}
public interface IHandler
{
	bool IsHandled { get; }
	void Handle();
}

public class FooHandler : IHandler
{
	public bool IsHandled { get; private set; }
	public void Handle()
	{
		IsHandled = true;
		Console.WriteLine(nameof(FooHandler) + ", " + nameof(Handle));
	}
}

public interface ILogger
{
	void Log(string line);
}

class InMemoryLogger : ILogger
{
	public void Log(string line)
	{
		Console.WriteLine(nameof(InMemoryLogger) + ": " + line);
	}
}

public class LoggingHandlerDecorator : IHandler
{
	private readonly IHandler _handler;
	public readonly ILogger Logger;

	public LoggingHandlerDecorator(IHandler handler, ILogger logger)
	{
		_handler = handler;
		Logger = logger;
	}

	public bool IsHandled => _handler.IsHandled;

	public void Handle()
	{
		Logger.Log("About to handle");
		_handler.Handle();
		Logger.Log("Successfully handled");
	}
}

2. 装饰使用服务键注册的服务

在多数情况下,注册装饰器时仅需要使用setup: Setup.Decorator参数。像注册正常的服务一样,还有其他的注册选项可供装饰器使用。serviceKey是不被支持的。原因是,不需要为装饰器指定sreviceKey,而是需要为指定了serviceKey的服务注册装饰器。
为了给通过serviceKey参数注册的服务应用装饰器,需要指定setup条件或者使用Decortator.Of方法。

2.1 使用Setup.DecoratorWith

void Main()
{
	var container = new Container();

	container.Register<IHandler, FooHandler>(serviceKey: "foo");
	container.Register<ILogger, InMemoryLogger>();
	container.Register<IHandler, LoggingHandlerDecorator>(setup: Setup.DecoratorWith(condition: request => "foo".Equals(request.ServiceKey)));

	var handler = container.Resolve<IHandler>("foo");
	Console.WriteLine(handler is LoggingHandlerDecorator);

	handler.Handle();
}

2.2 使用Setup.DecoratorOf

void Main()
{
	var container = new Container();

	container.Register<IHandler, FooHandler>(serviceKey: "foo");
	container.Register<ILogger, InMemoryLogger>();
	container.Register<IHandler, LoggingHandlerDecorator>(setup: Setup.DecoratorOf(decorateeServiceKey: "foo"));

	var handler = container.Resolve<IHandler>("foo");
	Console.WriteLine(handler is LoggingHandlerDecorator);

	handler.Handle();
}

Setup.DecoratorOf仅仅是DecoratorWith(condition:...)的封装,以提供更易使用的方式。

void Main()
{
	{
		var container = new Container();
		container.Register<ILogger, InMemoryLogger>();
		container.Register<IHandler, FooHandler>(serviceKey: "foo");
		container.Register<IHandler, BooHandler>(serviceKey: "boo");

		container.Register<IHandler, LoggingHandlerDecorator>(setup: Setup.DecoratorOf<BooHandler>(decorateeServiceKey: "boo"));

		var boo = container.Resolve<IHandler>("boo");
		boo.Handle();
	}

	{
		var container = new Container();
		container.Register<ILogger, InMemoryLogger>();
		container.Register<IHandler, FooHandler>();
		container.Register<IHandler, BooHandler>(serviceKey: "boo");

		container.Register<IHandler, LoggingHandlerDecorator>(setup: Setup.DecoratorOf<BooHandler>());

		var boo = container.Resolve<IHandler>("boo");
		boo.Handle();
	}
}
public interface IHandler
{
	bool IsHandled { get; }
	void Handle();
}

public class FooHandler : IHandler
{
	public bool IsHandled { get; private set; }
	public void Handle() {
		IsHandled = true;
		Console.WriteLine(nameof(FooHandler) + ", " + nameof(Handle));
	}
}

class BooHandler : IHandler
{
	public bool IsHandled { get; private set; }
	public void Handle() {
		IsHandled = true;
		Console.WriteLine(nameof(BooHandler) + ", " + nameof(Handle));
	}
}

interface ILogger { void Log(string line);}

class InMemoryLogger : ILogger {
	public void Log(string line) {
		Console.WriteLine(nameof(InMemoryLogger) + ": " + line);
	}
}

class LoggingHandlerDecorator : IHandler
{
	private readonly IHandler _handler;
	public readonly ILogger Logger;

	public LoggingHandlerDecorator(IHandler handler, ILogger logger) {
		_handler = handler;
		Logger = logger;
	}

	public bool IsHandled => _handler.IsHandled;

	public void Handle() {
		Logger.Log("About to handle");
		_handler.Handle();
		Logger.Log("Successfully handled");
	}
}

3. 嵌套装饰器

第一个注册的装饰器封装服务,后续注册的装饰器封装上一层的装饰器。

void Main()
{
	var container = new Container();

	container.Register<S>();
	container.Register<S, D1>(setup: Setup.Decorator);
	container.Register<S, D2>(setup: Setup.Decorator);

	var s = container.Resolve<S>();

	Console.WriteLine(s is S);		// True
	Console.WriteLine(s is D1);		// False
	Console.WriteLine(s is D2);		// True

	// ACTUALLY, you even can see how service is created yourself
	var expr = container.Resolve<LambdaExpression>(typeof(S));
	expr.Dump();
}

class S { }
class D1 : S { public D1(S s) { } }
class D2 : S { public D2(S s) { } }

在这里插入图片描述

3.1 指定嵌套顺序

void Main()
{
	var container = new Container();

	container.Register<S>();
	container.Register<S, D1>(setup: Setup.DecoratorWith(order: 2));
	container.Register<S, D2>(setup: Setup.DecoratorWith(order: 1));

	var s = container.Resolve<S>();

	Console.WriteLine(s is S);      // True
	Console.WriteLine(s is D1);     // True
	Console.WriteLine(s is D2);		// False

	// ACTUALLY, you even can see how service is created yourself
	var expr = container.Resolve<LambdaExpression>(typeof(S));
	expr.Dump();
}

class S { }
class D1 : S { public D1(S s) { } }
class D2 : S { public D2(S s) { } }

在这里插入图片描述

4. 开放式泛型服务装饰器

void Main()
{
	var container = new Container();
    container.Register(typeof(S<>));
    container.Register(typeof(S<>), typeof(D1<>), setup: Setup.Decorator);
    container.Register(typeof(S<>), typeof(D2<>), setup: Setup.Decorator);

    // decorator specific to the `S<string>` type
    container.Register<S<string>, DStr>(setup: Setup.Decorator);

    var intS = container.Resolve<S<int>>();
	Console.WriteLine(intS is D2<int>);	// True

	var strS = container.Resolve<S<string>>();
	Console.WriteLine(strS is DStr);	// True
}

class S<T> { }
class D1<T> : S<T> { }
class D2<T> : S<T> { }
class DStr : S<string> { }

5. 带类型参数的泛型的装饰器

装饰器可以通过工厂方法进行注册。这带了一个问题:如果工厂方法式开放式泛型且返回一个泛型参数类型时该怎么办?

public interface IStartable
{
	void Start();
}

public static class DecoratorFactory
{
	public static T Decorate<T>(T service) where T : IStartable
	{
		service.Start();
		return service;
	}
}

现在我们将使用Decorate方法注册应用于任何泛型类型参数T服务的装饰器。为此,我们需要注册一个object类型的装饰器,因为T在其定义的方法之外没有意义。

void Main()
{
	var container = new Container();
    container.Register<X>();

    // register with a service type of `object`
    container.Register<object>(
		made: Made.Of(req => typeof(DecoratorFactory).SingleMethod(nameof(DecoratorFactory.Decorate)).MakeGenericMethod(req.ServiceType)),
        setup: Setup.Decorator);

    var x = container.Resolve<X>();
}

public interface IStartable
{
	void Start();
}

public static class DecoratorFactory
{
	public static T Decorate<T>(T service) where T : IStartable
	{
		service.Start();
		return service;
	}
}

public class X : IStartable
{
	public bool IsStarted { get; private set; }
	public void Start() => IsStarted = true;
}

这个问题是,object类型的装饰器将被应用于所有的服务,这将影响解析的性能。为了让装饰器更有针对性,可以通过条件加以限制:

void Main()
{
	var container = new Container();
	container.Register<X>();
	
	var decorateMethod = typeof(DecoratorFactory).SingleMethod(nameof(DecoratorFactory.Decorate));
	
	container.Register<object>(
			made: Made.Of(r => decorateMethod.MakeGenericMethod(r.ServiceType)),
			setup: Setup.DecoratorWith(r => r.ImplementationType.IsAssignableTo<IStartable>()));

	// or we may simplify the condition via `DecoratorOf` method:
	container.Register<object>(
		made: Made.Of(r => decorateMethod.MakeGenericMethod(r.ServiceType)),
		setup: Setup.DecoratorOf<IStartable>());

	var x = container.Resolve<X>();
}

public interface IStartable
{
	void Start();
}

public static class DecoratorFactory
{
	public static T Decorate<T>(T service) where T : IStartable
	{
		service.Start();
		return service;
	}
}

public class X : IStartable
{
	public bool IsStarted { get; private set; }
	public void Start() => IsStarted = true;
}

6. 重用装饰器

装饰器可以像普通服务一样有自己的重用方式。通过这种方式,您可以仅通过应用装饰器向已注册的服务添加基于上下文的重用。

如果没有为装饰器指定重用,则表示该装饰器是默认容器重用 container.Rules.DefaultReuse(默认为 Reuse.Transient)。

void Main()
{
	var container = new Container();
    container.Register<S>();

    // singleton decorator effectively makes the decorated service a singleton too.
    container.Register<S, D1>(Reuse.Singleton, setup: Setup.Decorator);

    var s1 = container.Resolve<S>();
    var s2 = container.Resolve<S>();
	Console.WriteLine(s1 == s2);	// True
}

class S { }
class D1 : S { public D1(S s) { } }

6.1 使用UseDecorateeReuse参数指定装饰器的重用方式

此设置选项允许装饰器重用方式与装饰的服务相同。当你不希望调整装饰器的重用方式时,该选项是个不错的默认选项。

void Main()
{
	var container = new Container();

	container.Register<S>(Reuse.Singleton);
	container.Register<S, D1>(setup: Setup.DecoratorWith(useDecorateeReuse: true));

	var s1 = container.Resolve<S>();
	var s2 = container.Resolve<S>();
	Console.WriteLine(s1 == s2);	// True
}

class S { }
class D1 : S { public D1(S s) { } }
class D2 : S { public D2(S s) { } }

7. 装饰被封装的服务

装饰器可以应用于被封装的服务,以提供惰性、按需创建装饰服务、代理等。

void Main()
{
	var container = new Container();

	container.Register<ACall>();
	container.Register<ACall, LazyCallDecorator>(setup: Setup.Decorator);

	var a = container.Resolve<ACall>(); // A is not created.
	a.Call();                           // A is created and called.
}

class ACall
{
	public virtual void Call() { }
}

class LazyCallDecorator : ACall
{
	public Lazy<ACall> A;
	public LazyCallDecorator(Lazy<ACall> a) { A = a; }

	// Creates an object only when calling it.
	public override void Call() => A.Value.Call();
}

包装器的装饰器可以像普通服务的装饰器一样嵌套:

void Main()
{
	var container = new Container();

	container.Register<S>();
	container.Register<S, DLazy>(setup: Setup.Decorator);
	container.Register<S, DFunc>(setup: Setup.Decorator);

	var s = container.Resolve<S>(); // `s` is wrapped in Func with DFunc decorator
	s.Dump();

	s = ((DFunc)s).S();             // then its wrapped in Lazy with DLazy decorator
	s = ((DLazy)s).S.Value;         // an actual decorated `S` object
}


class S { }

class DLazy : S
{
	public Lazy<S> S { get; }
	public DLazy(Lazy<S> s) { S = s; }
}

class DFunc : S
{
	public Func<S> S { get; }
	public DFunc(Func<S> s) { S = s; }
}

在这里插入图片描述

8. 装饰封装器

DryIoc 支持直接装饰包装器以调整相应的包装器行为、添加新功能、应用过滤等。

void Main()
{
	var container = new Container();

	container.Register<I, A>();
	container.Register<I, B>();
	container.Register<I, C>(serviceKey: "test");

	// by default collection will contain instances of all registered types
	var iis = container.Resolve<IEnumerable<I>>();
	iis.Dump();
}


public interface I { }
public class A : I { }
public class B : I { }
public class C : I { }

在这里插入图片描述
为了排除以serviceKey指定的服务C,我们可以为IEnumerable<I>定义装饰器,它接受所有实例并过滤掉键控的东西:

void Main()
{
	var container = new Container();

	container.Register<I, A>();
	container.Register<I, B>();
	container.Register<I, C>(serviceKey: "test");

	container.Register<IEnumerable<I>>(
			Made.Of(() => GetNoneKeyed(Arg.Of<IEnumerable<KeyValuePair<object, I>>>())),
			setup: Setup.Decorator);
			
	var iis = container.Resolve<IEnumerable<I>>();
	iis.Dump();
}

static IEnumerable<I> GetNoneKeyed(IEnumerable<KeyValuePair<object, I>> all) => all.Where(kv => kv.Key is DefaultKey).Select(kv => kv.Value);
public interface I { }
public class A : I { }
public class B : I { }
public class C : I { }

在这里插入图片描述

9. 装饰器作为初始化器

使用 FactoryMethod 注册装饰器时,可以装饰服务的初始化管道。这意味着装饰器工厂方法接受注入的装饰服务,在被装饰的实例上调用一些初始化逻辑,并返回这个(或另一个)实例。

void Main()
{
	var container = new Container();

	container.Register<Foo>();
	container.Register<Foo>(Made.Of(() => DecorateFooWithGreet(Arg.Of<Foo>())), setup: Setup.Decorator);

	var foo = container.Resolve<Foo>();
	Console.WriteLine(foo.Message);
}

public class Foo
{
	public string Message { get; set; }
}

// a static method just for the demonstration
public static Foo DecorateFooWithGreet(Foo foo)
{
	foo.Message = "Hello, " + (foo.Message ?? "");
	return foo;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhy29563

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值