2

我有以下基本界面

public interface IHandler{
  void Handle(IMessage message);
}

和继承基本接口的通用接口

public interface IHandler<TMessage> : IHandler where TMessage : IMessage{
  void Handle(TMessage message);
}

我的类可以IHandler<TMessage>多次实现接口。IMessage是消息的基本接口,在这里不相关。目前我正在实现如下接口。

public class ExampleHandler : IHandler<ExampleMessage>, IHandler<OtherExampleMessag>{

  void IHandler.Handle(IMessage message){
    ExampleMessage example = message as ExampleMessage;

    if (example != null) {
      Handle(example);
    }
    else {
      OtherExampleMessage otherExample = message as OtherExampleMessage;

      if (otherExample != null) {
        Handle(otherExample);
      }
  }

  public void Handle(ExampleMessage) {
   //handle message;
  }

  public void Handle(OtherExampleMessage) {
   //handle message;
  }
}

困扰我的是我必须实现该Handle(IMessage)方法的方式,因为我认为它有很多冗余代码,并且每次IHandler<TMessage>在我的类上实现新接口时我都必须扩展该方法。

我正在寻找的是一种更通用的方法来实现该Handle(IMessage)方法(可能在处理程序的基类中),但我目前坚持如何做到这一点。

4

4 回答 4

3

您可以使用 newdynamic关键字将重载决议移至 DLR:

void IHandler.Handle(IMessage message)
{
    dynamic d = message;
    Handle(d);
}

请注意,RuntimeBinderException如果传入的消息对您的班级无效,这将在运行时失败。
为避免此异常,您可以为所有未知消息类型添加处理程序:

private void Handle(object unknownMessage)
{
    // Handle unknown message types here.
}

IHandler.Handle在基类中实现,您需要做更多的工作:

public class BaseHandler : IHandler
{
    void IHandler.Handle(IMessage message)
    {
        dynamic d = message;
        Handle(d);
    }

    private void Handle<TMessage>(TMessage message) where TMessage : IMessage
    {
        var handler = this as IHandler<TMessage>;
        if(handler == null)
            HandleUnknownMessage(message);
        else
            handler.Handle(message);
    }

    protected virtual void HandleUnknownMessage(IMessage unknownMessage)
    {
        // Handle unknown message types here.
    }
}

您的特定处理程序将如下所示:

public class ExampleHandler : BaseHandler,
                              IHandler<ExampleMessage>,
                              IHandler<OtherExampleMessage>
{
    public void Handle(ExampleMessage message)
    {
        // handle ExampleMessage here
    }

    public void Handle(OtherExampleMessage message)
    {
        // handle OtherExampleMessage here
    }
}

这段代码现在像这样工作:

  1. DLR 调用BaseHandler.Handle<TMessage>具有真实消息类型的泛型方法,即TMessage不会是IMessage具体的消息类,如ExampleMessage.
  2. 在此通用处理程序方法中,基类尝试将自己的大小写为特定消息的处理程序。
  3. 如果不成功,它会调用HandleUnknownMessage来处理未知的消息类型。
  4. 如果转换成功,它会调用Handle特定消息处理程序上的方法,有效地将调用委托给具体的处理程序实现。
于 2012-10-12T09:29:19.593 回答
1

一种合理的方法是明智地使用反射:

var method = this.GetType().GetMethod("Handle", new[] { message.GetType() });

if (method != null) {
    method.Invoke(this, new[] { message });
}

如果您这样做太多以至于性能成为问题,您可以缓存测试结果以进行大规模改进。

于 2012-10-12T09:27:42.523 回答
1

你卡住了,因为你的班级(在问题中)做的不止一件事。它处理ExampleMessageOtherExampleMessage。我建议你创建一个类来处理一件事。

例子:

public class ExampleHandler : IHandler<ExampleMessage> 

public class OtherExampleHandler : IHandler<OtherExampleMessag>

据我了解,您希望有一个类来处理某种事件。在这种情况下,您可能必须使用观察者模式来通知每个 Handler 当有事情发生并让他们做他们的工作。

于 2012-10-12T09:33:56.243 回答
-1

接口是说你有一个提供 N 服务的实例。当然,这些服务是相似的,但由于它们针对不同的类型,它们是独立的服务。所以你检测到“代码气味”。气味是“为什么不同服务的通用方法?”。

那么服务是否足够不同以证明通用接口声明的合理性?这里的基础是“复制”。重构出重复。重复是不好的。一旦你把重复的东西移出,答案就会不言而喻。

换句话说,摆脱通用方法并以自己的方法处理每个方法......重复是您想要移出到另一个类的内容。如果是这样,请考虑注射。

喜欢你的气味检测!

于 2012-10-12T09:33:57.530 回答