程序设计实例(3)

  最近在指导一个新同事完成一个C#的程序,本文是我对他程序在设计上的一些意见。

1.要求:

  做一个等待后台长时间操作的控件,在等待过程中该控件要显示一个等待画面,后台处理完成后自动关闭。

2.过程:

  该新同事很快完成了任务,原因是他从网上下载了一个现成的Demo。我检查后确认:该Demo满足我的要求,并且该同事也理解了其源代码,因此我承认他已经完成了任务。

3.分析:

  由于该源代码来自网上,代表一般的软件代码,提一些我的意见。

  首先,来看一下源代码(下载):源代码主要有3个类:

    MainWindow:主画面

    WaitingDlg:显示等待画面;

    ILongTimeTask:后台长时间操作的任务接口,调用者需要实现该接口。

    调用方式:    

            WaitingDlg dlg =new WaitingDlg(new LongTimeTaskAbc());
            dlg.Owner = this;
            dlg.ShowInTaskbar = false;
            dlg.ShowDialog();

  其中的LongTimeTaskAbc是继承ILongTimeTask,由用户实现后台长时间操作任务。从结构上来看是相当简单,合理的。

  WaitingDlg:  

   public delegate void TaskEndNotify(Object result);

    public partial class WaitingDlg
    {
        public Object TaskResult{get { return m_taskResult; }}

        private Object m_taskResult;
        private bool m_bCloseByMe;
        private readonly ILongTimeTask m_task;
        private readonly string m_strThePromptText;
        
        public WaitingDlg(ILongTimeTask task, string strThePromptText=null)
        {
            m_task = task;
            m_strThePromptText = strThePromptText;
            InitializeComponent();

        }

        private delegate void CloseMethod();

        public void TaskEnd(Object result)
        {
            m_taskResult = result;
            m_bCloseByMe = true;
            Dispatcher.BeginInvoke(new CloseMethod(Close));
        }
        
        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (!m_bCloseByMe) { e.Cancel = true; }
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            if (!string.IsNullOrEmpty(m_strThePromptText))
            {
                tbPrompt.Text = m_strThePromptText;
            }
            m_task.Start(this);
        }

  LongTimeTaskAbc(调用者实现)

  class LongTimeTaskAbc : ILongTimeTask
    {
        private Thread m_threadWorking;
        private WaitingDlg m_dlgWaiting;

        public void Start(WaitingDlg dlg)
        {
            m_dlgWaiting = dlg;
            m_threadWorking = new Thread(Working);
            m_threadWorking.Start();
        }

        private void Working()
        {
            Thread.Sleep(2000);
            m_dlgWaiting.TaskEnd(null);
        }
    }

  从以上源代码看出了:WaitingDlg包含了一个ILongTimeTask对象,调用了ILongTimeTask.Start(),LongTimeTaskAbc也包含了一个WaitingDlg对象,调用了WaitingDlg.TaskEnd().这种相互引用和调用我是坚决反对的。作者的目的无非是在WaitingDlg中启动ILongTimeTask.Start(),在LongTimeTaskAbc中,处理结束时调用WaitingDlg.TaskEnd()来关闭等待画面。完全可以使用回调机制或事件通知来处理这种问题,降低模块间的耦合度。别忘了LongTimeTaskAbc还是一个用户模块,和你的控件模块高度耦合是不对的。

  另外一个问题就是:LongTimeTaskAbc中的Start()函数,该函数就是启动一个线程来完成后台长时间操作。这个控件的核心就是启动一个线程来完成后台长时间操作,而LongTimeTaskAbc是用户模块,也就是说你写个控件,核心还得用户来帮你完成,搞毛啊!并且使用上也可以看出问题:用户每次创建一个新的成后台操作任务,都要重复实现ILongTimeTask.Start()的相同代码。一种解决的方法是把ILongTimeTask变成基类,实现Start()的功能,但更好的方法是把ILongTimeTask.Start()放在WaitingDlg中去实现,这才更符合程序设计原则。

  

    

 

posted on 2013-06-05 11:46  钟湘光  阅读(409)  评论(1编辑  收藏  举报

导航