DataTracker -- 窗体之间的联动, 观察者模式的另类实现

DataTracker, 是本人在编程实践中为了解决窗体之间的联动而实现的工具类。

主要用于解决:在一个窗体中修改了数据,而另一个使用此数据的窗体实现自动的数据更新。

它有以下特点:

1) 对象之间的松藕合性。两个窗体或多个窗体之间,相互之间不需要知道对方的存在。

2) 可以实现同一应用程序中,多个子窗体之间的联动;

3) 也可以实现分布式的联动。即A计算机修改了数据,依赖此数据的B计算机中的窗体进行自动刷新。

 

假如此DataTracker进行了封装可以直接调用,首先看用法:

 

接口定义:

    
/// <summary>
    /// 定义可以刷新的窗体或控件的接口
    /// </summary>
    public interface ICanReLoad
    {
        /// <summary>
        /// 刷新的窗体或控件
        /// </summary>
        void ReLoad();
    }

    /// <summary>
    /// 可以跟踪别的对象的修改并触发默认行为
    /// </summary>
    public interface ITracker : ICanReLoad
    {
        /// <summary>
        /// 被跟踪对象的字符串名称表,以逗号分隔
        /// </summary>
        string TrackTypes { get; }
        /// <summary>
        /// 指示是否是在被跟踪对象改变时是否立即重新获取数据
        /// 如果为否则等控件在被激活时才刷新。
        /// </summary>
        bool FastReload { get; set; }
    }

    /// <summary>
    /// 可以跟踪别的对象的修改并触发指定名称的行为
    /// </summary>
    public interface INamedTracker : ITracker
    {
        /// <summary>
        /// 刷新窗体或控件
        /// </summary>
        /// <param name="typeName">指定的名称</param>
        void ReLoad(string typeName);
    }

也就是说,要实现窗体之间的联动,各联动窗体要实现以上接口。如果是整个窗体的刷新,实现ITracker接口,实现局部刷新,实现INamedTracker接口。

实现以上接口以上,整个应用程序的联动管理类是DataTracker工具类。它实现总控。它的方法有:

        /// <summary>
        /// 源对象在修改时通知对应的跟踪它的对象
        /// </summary>
        /// <param name="sourceTypes">用逗号隔开的数据类型名称列表</param>
        public static void Change(params string[] sourceTypes)

        /// <summary>
        /// 通知本地和远程同时更新界面数据
        /// </summary>
        /// <param name="sourceTypes">要更新的数据名称列表</param>
        public static void ChangeWithRemote(params string[] sourceTypes)
  
        /// <summary>
        /// 判断并刷新指定的跟踪对象
        /// </summary>
        /// <param name="tracker"></param>
        public static void ReLoad(ITracker tracker)

        /// <summary>
        /// 判断并刷新指定名称的跟踪对象
        /// </summary>
        /// <param name="tracker"></param>
        /// <param name="typeName"></param>
        public static void ReLoad(INamedTracker tracker, string typeName)

        /// <summary>
        /// 注册跟踪对象
        /// </summary>
        /// <param name="tracker"></param>
        public static void Register(ITracker tracker)

        /// <summary>
        /// 反注册跟踪对象
        /// </summary>
        /// <param name="tracker"></param>
        public static void UnRegister(ITracker tracker)


以上限于篇幅,只列出了DataTracker中的方法签名并没有列出实现。它的实现也很简单,大体是维护一个跟踪列表,当收到用Change()方法接收的联动信号以后,

遍历跟踪表,依次调用窗体的ReLoad()方法实现联动。

 

总之,使用它的方法是:

1) 窗体实现ITracker接口或INamedTracker接口, 其中,TrackTypes属性中列出本窗体有关的数据项名称。这个数据项名称可以任意,但是必须和发送方的名称相同。

2) 用DataTracker.Register()方法注册本窗体到跟踪列表中。

3 ) 什么都不管了,坐等其他窗体发信息。

 

示例:

//窗体A, 展示数据的窗体
public class FormA:Form,ITracker
{
	public string TrackTypes { get {return "数据A";} }

	
	public FormA()
	{    
	    InitializeComponent();
	    DataTracker.Register(this);
	}
	

	public void ReLoad()
        {
            //刷新数据A
        }
	
	protected void FormA_Closed(object sender, EventArgs e)
	{//对于窗体或控件而言,如果不在关闭时注销跟踪项,后果会很严重

	    DataTracker.Unregister(this);
	}
}

//窗体B, 修改了数据的窗体
public class FormB:Form
{
	public void SaveDataA()
	{
		//修改数据A
		// ...

		//发出信号
		DataTracker.Change("数据A");
	}
} 


以上是实现ITracker接口的窗体联动的例子,最为简单。

如果实现INamedTracker接口,则稍稍复杂一点,可能要用一系列case语句来路由判断要刷新哪一部分数据。这里就先不赘述了。

以上FormA中的Register和UnRegister等方法,可以在基类窗体中作进一步封装,以进一步简化业务窗体的使用。

毕竟,要实现一个复杂的应用程序,很少有窗体只用到Form这个基类的。

 

下面还有最激动人心的,分布式的联动,我可以把这部分代码整个晒出来。因为很简单:

    /// <summary>
    /// 远程跟踪类:用于远程通知并接收数据的更新信号
    /// </summary>
    public class RemoteTracker
    {
        /// <summary>
        /// 最后一次更新的时间
        /// </summary>
        DateTime lastUpdateTime = ServerHelper.ServerTime;

        /// <summary>
        /// 从服务端获取数据的更新信号
        /// </summary>
        /// <returns></returns>
        string GetRemoteChangedTypes()
        {
            string r = WebHelper.GetWebResponseText(
                String.Format("{0}/DataTrackerService.ashx?dt={1}&{2}",
                XpoFactory.Instance.ServiceUrl,
                HttpUtility.UrlEncode(lastUpdateTime.ToString("yyyy-MM-dd HH:mm:ss")),
                LoginMgr.Instance.VerifyQuery));
            return r;
        }

        /// <summary>
        /// 将本地数据的更新信号发到服务端
        /// 再由服务端通知其他客户端更新
        /// </summary>
        /// <param name="trackTypes">发生变更的数据名称列表 </param>
        public string TellRemoteChanged(params string[] trackTypes)
        {
            string r = WebHelper.GetWebResponseText(
                String.Format("{0}/DataTrackerService.ashx?tt={1}&dt={2}&{3}",
                XpoFactory.Instance.ServiceUrl,
                HttpUtility.UrlEncode(String.Join(",", trackTypes)),
                HttpUtility.UrlEncode(ServerHelper.ServerTime.ToString("yyyy-MM-dd HH:mm:ss")),
                LoginMgr.Instance.VerifyQuery));
            return r;
        }

        System.Timers.Timer timer = new System.Timers.Timer(10000); //默认10秒一次

        /// <summary>
        /// 远程跟踪对象单例
        /// 用于远程通知并接收数据的更新信号
        /// </summary>
        public static RemoteTracker Instance = new RemoteTracker();

        private RemoteTracker()
        {
        }

        /// <summary>
        /// 开始执行跟踪
        /// </summary>
        public void Start()
        {
            timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
            timer.Start();
        }

        void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            string types = GetRemoteChangedTypes();
            if (!string.IsNullOrEmpty(types))
            {
                DataTracker.Change(types.Split(','));
            }
            lastUpdateTime = ServerHelper.ServerTime;
        }
    }


事实上,分布式联动其实就是一个轮询机制,每隔一段时间请求一次服务器(这里用的asp.net的ashx服务),获取最近修改过的数据列表。

在联动的发起方,要做的事情是在修改数据后,调用TellRemoteChanged()方法来通知服务器。

另外,在DataTracker.ChangeWithRemote()方法中,已经综合调用了本地通知和远程通知,因此,通过这个方法可以同时发起本地的联动和远程其他客户机的联动。

 

虽然此工具类处理窗体是最典型的应用,它也可以处理其他任何object对象,从而实现一些非常巧妙的组合应用。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值