我可以使用装饰器模式来包装方法体吗?

时间:2010-06-03 14:10:04

标签: c# design-patterns decorator

我有一堆不同签名的方法。这些方法与脆弱的数据连接交互,因此我们经常使用辅助类来执行重试/重新连接等。如下所示:

MyHelper.PerformCall( () => { doStuffWithData(parameters...) });

这样可以正常工作,但它可以使代码变得有点混乱。我更喜欢做的是装饰与数据连接交互的方法,如下所示:

[InteractsWithData]
protected string doStuffWithData(parameters...)
{
     // do stuff...
}

然后基本上,每当调用doStuffWithData时,该方法的主体将作为Action传递给MyHelper.PerformCall()。我该怎么做?

7 个答案:

答案 0 :(得分:16)

.NET属性是元数据,而不是自动调用的装饰器/活动组件。没有办法实现这种行为。

您可以使用属性来实现装饰器,方法是将装饰器代码放在Attribute类中,并使用辅助方法调用该方法,该方法使用Reflection调用Attribute类中的方法。但是我不确定这会比直接调用“装饰器方法”有很大改进。

“装饰-属性”:

[AttributeUsage(AttributeTargets.Method)]
public class MyDecorator : Attribute
{
    public void PerformCall(Action action)
    {
       // invoke action (or not)
    }
}

方法:

[MyDecorator]
void MyMethod()
{
}

用法:

InvokeWithDecorator(() => MyMethod());

助手方法:

void InvokeWithDecorator(Expression<Func<?>> expression)
{
    // complicated stuff to look up attribute using reflection
}

查看C#中面向方面编程的框架。这些可能会提供你想要的东西。

答案 1 :(得分:16)

所以,我本周末刚刚参加了AOP会议,这是使用PostSharp进行的一种方式:

[Serializable]
public class MyAOPThing : MethodInterceptionAspect
{
    public override void OnInvoke(MethodInterceptionArgs args)
    {
        Console.WriteLine("OnInvoke! before");
        args.Proceed();
        Console.WriteLine("OnInvoke! after");
    }
}

然后用[MyAOPThing]装饰方法。简单!

答案 2 :(得分:4)

如果不使用gode生成,则无法对其进行多少操作。你可以使语法更好。

但是使用扩展方法呢?

class static MyHelper
{
  Wrap<T>(this object service, Action<T> action)
  {
    // check attribute and wrap call
  }

}

用法:

RawFoo foo = ...
foo.Wrap(x => x.doStuffWithData(parameters...));

这很简单,但你无法确保使用了Wrap。

您可以实现通用装饰器。这个装饰器将用于包装服务,然后你不能在没有包装的情况下调用它。

class Decorator<T>
{
    private T implementor;

    Decorator(T implementor)
    {
      this.implementor = implementor;
    }

    void Perform<T>(Action<T> action)
    {
      // check attribute here to know if wrapping is needed
      if (interactsWithData)
      {
        MyHelper.PerformCall( () => { action(implementor) });
      }
      else
      {
        action(implementor);
      }
    }
}

static class DecoratorExtensions
{
    public static Decorator<T> CreateDecorator<T>(T service)
    {
      return new Decorator<T>(service);
    }
}

用法:

// after wrapping, it can't be used the wrong way anymore.
ExtendedFoo foo = rawFoo.CreateDecorator();
foo.Perform(x => x.doStuffWithData(parameters...));

答案 3 :(得分:3)

这类问题几乎就是AOP(面向方面​​编程)旨在解决的问题。 PostSharp等工具可以通过重写编译代码来提供跨领域的关注。 Scott Hanselman的播客最近讨论了AOP,所以值得倾听。

答案 4 :(得分:1)

结帐aspect oriented frameworks。但请注意,虽然它们隐藏了每种方法的复杂性,但AoP功能的存在可能会使您的程序难以维护。这是一个权衡。

答案 5 :(得分:1)

看起来你想要的东西类似于IoC容器或测试运行器框架的行为,它不是从程序集中实际执行,而是运行围绕代码构建的动态发出的程序集。 (比我在其他答案中称之为AOP更聪明的人)

因此,也许在您的应用程序的存根中,您可以扫描其他程序集,构建那些发出的程序集(使用装饰方法的主体调用MyHelper.PerformCall),然后您的程序将针对发出的代码运行。

在没有评估一些现有的AOP框架是否可以实现您的需求的情况下,我决不会开始尝试编写本文。 HTH&GT;

答案 6 :(得分:0)

看到你愿意为每个需要它的方法添加一行代码,为什么不从方法本身调用MyHelper,就像这样?

protected string doStuffWithData(parameters...)
{
     MyHelper.PerformCall( () => { doStuffWithDataCore(parameters...) });
}

private string doStuffWithDataCore(parameters...) {
    //Do stuff here
}