首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >对于WPF窗口中介服务,用简单注入器解析实例的替代方案是什么?

对于WPF窗口中介服务,用简单注入器解析实例的替代方案是什么?
EN

Stack Overflow用户
提问于 2015-12-09 22:21:28
回答 1查看 1.2K关注 0票数 4

查看简单喷射器网站上的"按键解析实例“部分,我使用了建议的IRequestHandlerFactory实现来改进下面的代码,但注意事项如下:

注意:键控注册的需要可能是应用程序设计中含糊不清的迹象,也是Liskov替代原则违反的标志。仔细观察一下,如果每个键控注册不应该有自己独特的接口,或者每个注册都应该实现自己版本的通用接口。

注意:请记住之前关于应用程序设计中含糊不清的注释。在给定的示例中,通过使用通用的IRequestHandler接口,设计可能会更好。这将允许使用一行代码对实现进行批处理注册,避免使用密钥,并产生由容器验证的配置。

让我好奇。

Q:说明中提到的实际实现(IRequestHandler)将如何看待我的情况?我花了一段时间试图弄清楚它可能是什么,但却想不出一个可行的方法。

我现在拥有的

为了解耦视图模型,我目前有一个视图和对话框创建者,它通过MVVMLight的Messenger监听消息,并且基于这些消息将创建所请求的必要的Windows或对话框。

我注入了一个IWindowResolver,它是一个工厂(实际上是一个服务定位器),它使用组合根目录上的简单Injector工厂模式来获取请求的窗口类型。

我喜欢RegisterViewHandler,因为它清楚地将消息、相关窗口和消息处理程序拼接在一起,而不是在代码中传播(例如,HandleEmailPopupMessage不需要知道要获取的窗口的确切类型,HandleEmailPopupMessage使用的模式也可以作为简单窗口创建和消息发送的通用模式)。然而,我相信IWindowResolver可能不是一个定位器,更多的工作可能是将一些注册推入复合根。

次要问题--某种IRequestHandler更容易/更健壮/更有用吗?还是简单地将字典推倒到工厂级别,就像简单的Injector doco给出的其他例子一样,足够整理了吗?

代码语言:javascript
复制
// Current IWindowResolver
interface IWindowResolver
{
   Window CreateWindow<TWindow>(TWindow windowType) where TWindow : class;
}

// Current Simple Injector IWindowResolver implementation
[UsedImplicitly]
private sealed class SimpleInjectorWindowFactory : IWindowFactory
{
   private readonly Container _container;

   public SimpleInjectorWindowFactory(Container container)
   {
      _container = container;
   }

   public Window CreateWindow<TWindow>(TWindow windowType) where TWindow : class
   {
      return _container.GetInstance<TWindow>() as Window;
   }
}

public class ShowEmailPopupFormMessage
{
   public ShowEmailPopupFormMessage()
   {
      Params = new ParamsMessage();
   }

   public class ParamsMessage
   {
      public string CustomerName { get; set; }
      public string EmailTo { get; set; }
   }

   public ParamsMessage Params { get; set; }
}

// Current ViewConstructor
class ViewConstructor
{
   IWindowResolver _windowResolver;
   Dictionary<Type, Type> _viewMap = new Dictionary<Type, Type>(); // Maps a message type to a particular window/view type

   public ViewConstructor(IWindowResolver windowResolver)
   {
      _windowResolver = windowResolver;
      RegisterViewHandler<ShowEmailPopupFormMessage, EmailPopupWindow>(HandleEmailPopupMessage);
   }

   private void RegisterViewHandler<TMessage, TWindow>(Action<TMessage> messageAction)
      where TMessage : class
      where TWindow : Window
   {
      if (_viewMap.ContainsKey(typeof(TMessage)))
      {
         throw new ArgumentException("View already registered");
      }

      // Store the map of Message type to Window type
      _viewMap[typeof(TMessage)] = typeof(TWindow);

      // Register with the message handler
      Messenger.Default.Register(this, messageAction);
   }

   private void HandleEmailPopupMessage(ShowEmailPopupFormMessage msg)
   {
      var frm = GetMappedWindow(msg.GetType());

      // We know that the View and it's associated ViewModel are now created
      // so we can send some initialization parameters to the view and or ViewModel
      Messenger.Send(msg.Params);

      frm.ShowDialog();
   }

   private Window GetMappedWindow<TMessage>(TMessage messageType)
   {
      var windowType = _viewMap[typeof(TMessage)];

      var frm = _windowResolver.CreateWindow(windowType);

      if (frm == null)
      {
         throw new ApplicationException("Window is not of the specified Type!");
      }

      // Hookup common events such as cleanup events
      frm.Unloaded += FormOnUnloaded;

      return frm;
   }

   private static void FormOnUnloaded(object sender, RoutedEventArgs eArgs)
   {
      var frm = sender as Window;

      if (frm == null)
      {
         return;
      }

      // Cleanup the ViewModel 
      var dataContext = frm.DataContext as ICleanup;

      if (dataContext != null)
      {
         dataContext.Cleanup();
      }
   }
}

public class EmailPopupWindow : Window
{
   // Window knows how to set it's datacontext's ViewModel (in this case EmailPopupVm) using the ViewModelLocator declared in XML.
   // The window does not handle any messages.
}

// The View Model for the EmailPopupWindow. ViewModelBase is from MVVMLight
public class EmailPopupVm : ViewModelBase
{
   public EmailPopupVm()
   {
      Messenger.Register<ShowEmailPopupFormMessage.ParamsMessage>(HandleParamsMessage);
   }

   private void HandleParamsMessage(ShowEmailPopupFormMessage.ParamsMessage msg)
   {
      // Initialize the ViewModel with the parameters
      this.CustomerName = msg.CustomerName;
      this.EmailTo = msg.EmailTo;
   }
}

更新

为了清晰起见,ViewModel (现在添加到上面的示例代码中)实际上处理了ShowEmailPopupFormMessage.ParamsMessage。窗口对任何信息都没有察觉。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-12-10 06:56:53

通常很难深入研究某个特定设计的细节,并在应用程序的上下文中提取出一些实际正确的内容,因为很多细节通常都是缺失的。但无论如何我还是会试一试的,所以如果我有点不舒服的话,我会提前道歉的。

在我看来,您缺少一个可以处理消息的类的抽象;让我们称之为IMessageHandler<TMessage>

代码语言:javascript
复制
public interface IMessageHandler<TMessage>
{
    void Handle(TMessage message);
}

接下来,windows似乎也链接到某些消息。因此,我将很好地为windows定义一个通用接口,例如:

代码语言:javascript
复制
public interface IWindow<TMessage>
{
    void ShowDialog();
}

接下来,您可能需要一些允许您发送和发送消息的抽象(尽管MVVMLight可能已经给您提供了这种抽象,但我对此并不熟悉):

代码语言:javascript
复制
public interface IMessageDispatcher
{
    void Dispatch(object message);
}

基于IMessageHandler<TMessage>抽象,我们现在可以创建一个可以处理ShowEmailPopupFormMessage的处理程序。

代码语言:javascript
复制
public class ShowEmailPopupFormMessageHandler : IMessageHandler<ShowEmailPopupFormMessage>
{
    private readonly IWindow<ShowEmailPopupFormMessage> frm;
    public ShowEmailPopupFormMessageHandler(IWindow<ShowEmailPopupFormMessage> frm) {
        this.frm = frm;
    }

    public void Handle(ShowEmailPopupFormMessage message) {
        Messenger.Send(msg.Params);
        frm.ShowDialog();
    }
}

如果您的EmailPopupWindow处理ShowEmailPopupFormMessage消息,我们应该让它实现IWindow<ShowEmailPopupFormMessage>

代码语言:javascript
复制
public class EmailPopupWindow : Window, IWindow<ShowEmailPopupFormMessage>
{
    // window stuff here
}

现在剩下的是将所有东西连接到组合根目录中:

代码语言:javascript
复制
// Composition Root
container.Register(typeof(IWindow<>), applicationAssemblies);
container.Register(typeof(IMessageHandler<>), applicationAssemblies);
container.RegisterSingleton<IMessageDispatcher>(
    new SimpleInjectorMessageDispatcher(container));
container.RegisterInitializer<Window>(frm => {
    frm.Unloaded += FormOnUnloaded;
});

注意,IWindow<T>IMessageHandler<T>实现都是使用批处理注册连接起来的。还请注意,Window.Unload事件的注册是在复合根中完成的。

SimpleInjectorMessageDispatcher也是组合根的一部分:

代码语言:javascript
复制
private sealed class SimpleInjectorMessageDispatcher : IMessageDispatcher
{
    private readonly Container container;
    public SimpleInjectorMessageDispatcher(Container container) {
        this.container = container;
    }

    public void Dispatch(object message) {
        Type handlerType = typeof(IMessageHandler<>).MakeGenericType(message.GetType());
        dynamic handler = this.container.GetInstance(handlerType);
        handler.Handle((dynamic)message);
    }
}

就像我说的,我可能有一点(或一英里),但希望这将为您提供一些关于如何使用泛型类型来处理这个问题的想法。泛型类型具有这样的优点:它为您提供了一致的设计,允许在类型的定义中刻录元数据,简化了应用横切关注点(使用装饰器),并允许轻松注册。但同样的约束也适用于一般抽象;它们必须遵循坚实的原则。因此,它们必须是集中和狭窄的(最好只有一个成员)。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34190128

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档