访问外部聚合类的最佳实践
本文关键字:最佳 外部 访问 | 更新日期: 2023-09-27 18:03:10
我目前正在开发一个WPF应用程序,它有一个主窗口,其中包含一个面板,其内容可以交换以显示不同的用户控件。基本上,这只是意味着我有一个带有经常可见的侧栏的菜单。
我的问题是关于减少类之间的耦合。我有一个通知部分(在一个列表框的形式)在我的主窗口,消息可以弹出。通知可以由某些用户控件(UCs)中发生的操作"触发"。这些用户控件当然是主窗口类的成员。
由于我需要影响主窗口类中的ListBox控件,我想知道最佳实践将是什么。显然,最简单的解决方案是通过每个UCs的构造函数将对Main Window的引用传递给它们,但这在很多层面上似乎不是很有效。当然,我只能传递ListBox元素,但这也不能真正起作用,因为我必须对添加到ListBox中的数据执行操作,因此我必须在每个UC中以编程方式重复这些指令。
我可以使用一个引用ListBox并实现通知方法的单例,但是我已经在我的项目中有了更多我满意的单例(我不希望每个人都能访问这些方法,只有某些UCs)。
另一种方法是通过相关UCs的构造函数传递Notification Manager实例(它将完成相同的工作)。该管理器将依次拥有将UCs链接到主窗口的所有必要方法。
可能有我没有想到的其他更有效的解决方案。我想知道您对这种情况下的最佳实践的看法,特别是考虑到它生成的类耦合。它可能与这个特定的问题有关,但是要以更一般的方式考虑它,其中内部聚合类需要访问外部的一些资源。谢谢。
我喜欢Jesse提出事件的想法:它非常符合wpf,并且允许需要记录内容的UserControl
和提供日志记录的窗口之间的松耦合。此外,做日志记录的UserControls不依赖于日志服务是否可用,这将使解决方案更加灵活。
这是如何工作的
需要记录日志的UserControl
只是引发一个冒泡事件,其中包含相关信息(要记录的文本消息)。UC将不知道该消息将如何处理,甚至不知道该消息是否将被处理,它只是提出它。这是一种轻量级的操作,耦合最小。
凸起的消息沿着层次结构链向上冒泡,一直到顶层元素(Window
, Page
)。在此过程中的任何地方,元素都可以为此事件类型提供处理程序,并通过它获得有关日志请求的通知。同样,耦合是非常松散的:实现的地方并不关心谁发送消息。可以是UserControl或者其他任何东西
如果UIElement
可用作日志消息事件的起源,则此操作绝对有效。
这里是一步一步的实现:
LogServices类我们不能为了这个目的重用一个已经存在的事件,需要创建一个新的事件(为了避免混淆)。应该这样做:
// Subclass of RoutedEventArgs that allows us to easily and nicely pass the message
// to be logged
public class LogEventArgs : RoutedEventArgs
{
public string Msg { get; set; }
public LogEventArgs(RoutedEvent routedEvent, Object sender, string Msg)
:base(routedEvent, sender)
{
this.Msg = Msg;
}
}
// This is the Delegate that's used to grab the Log message
public delegate void RoutedLogEventHandler(Object sender, RoutedEventArgs e);
// This works as the abstraction layer that will allow UC's to raise LOG messages
// and allow your implementation to alter the way it handles those LOG messages.
// Since we're doing this with a routed event, we need an DependencyObject to
// reigster it.
public class LogServices :UIElement
{
public static RoutedEvent LogEvent;
// Static constructor, registers the event
static LogServices()
{
LogEvent = EventManager.RegisterRoutedEvent("Log", RoutingStrategy.Bubble, typeof(RoutedLogEventHandler), typeof(UIElement));
}
// This helps raise the relevant shared event
public static void RaiseLog(string Msg, UIElement sender)
{
sender.RaiseEvent(new LogEventArgs(LogEvent, sender, Msg));
}
}
上面的代码声明了RoutedEventArgs
的子类,因为我们需要传递字符串Message。然后,它创建新的委托类型,该类型接受我们的LogEventArgs
参数,最后从LogServices
的静态构造函数注册事件。
如何发送事件
发送事件超级简单。这是按钮的Click处理程序:
LogServices.RaiseLog("Message to be logged.", sender as Button)
接收事件
我们的事件被注册为"冒泡"事件:它将从我们提出它的控件开始,并给每个父节点一个处理它的机会,一直到Window。最简单的方法是在执行日志记录的Window
中处理事件。不幸的是,这种类型的共享不能直接从XAML
中设置,它需要从代码中分配。这就是我如何尝试从XAML
分配它,它没有工作:
<Grid local:LogService.Log="HandlerName" />
下一个选项是从代码中分配它。这是我的测试Window1
的一个片段:
// Window Constructor
public MainWindow()
{
InitializeComponent();
// Set the event handler.
base.AddHandler(LogServices.LogEvent, new RoutedLogEventHandler(HandleMsgLog));
}
// This is the actual handler.
public void HandleMsgLog(Object sender, RoutedEventArgs e)
{
// Put the received message into the ListBox
LB.Items.Add((e as LogEventArgs).Msg);
}
你可以阅读观察者模式(http://en.wikipedia.org/wiki/Observer_pattern),但这种模式比你的情况更广泛使用。
在你的情况下,我认为好的变体是:
1)另一种方法是通过相关UCs的构造函数传递Notification Manager实例(该实例将执行相同的工作)。该管理器将依次拥有将UCs链接到主窗口的所有必要方法。(c)
2)所有UC必须实现带有'NotificationRaised'等事件的接口,并且在主窗口中需要订阅此事件。Jesse在对你的问题的评论中说道