C#,将消息显示代码与业务逻辑分开

时间:2011-09-20 03:36:00

标签: c# winforms

我有一个winforms应用程序,我没有遵循任何设计模式。我的问题是,我有这些基类包含我的所有业务逻辑。当发生异常或我需要向用户显示对话框时,我已将代码直接写入我需要它的基类中。

我知道我需要将业务逻辑和显示逻辑分开,所以我编写了一个静态类,其中包含了显示消息所需的方法。

我的问题是,是否有更简单的方法将业务逻辑与显示分开?

我的静态方法看起来像这样,

public static void DisplayMessage(string message) 
    {
        MessageBox.Show(message);
    }

 public static bool DisplayDialogBox(string message,string caption ) 
    {
        DialogResult newresult = new DialogResult();

        newresult = MessageBox.Show(message,caption,MessageBoxButtons.OKCancel);

        if (newresult == DialogResult.OK)
        {
            return true;
        }
        else 
        {
            return false;
        }  

所以我将从基类调用这些方法,比如

MsgDisplay.DisplayMessage(e.Message);

这种方法是一种很好的做法吗?

5 个答案:

答案 0 :(得分:2)

不,这种方法不是一个好习惯。您的类和MessageBox类之间没有任何区别 请与您的班级见:

MsgDisplay.DisplayMessage(e.Message);

并且只使用MessageBox

MessageBox.Show(e.Message);

此包装器不提供任何其他功能 如果要分离业务逻辑和ui,则必须分解方法并仅在UI层中显示消息 而且,小点。而不是:

if (newresult == DialogResult.OK)
    {
        return true;
    }
    else 
    {
        return false;
    }

只输入:

return newresult==DialogResult.OK

<强>更新 如果要显示的只是异常消息,那么您应该捕获异常并在UI层上显示消息。因此,在您的业务类中,而不是显示消息:

void foo() {
   try {
       //some code here
   }
   catch(FooException fe) {
       MessageBox.Show(fe.Message);
   }
}

将异常抛出到ui层:

void foo() {
   try {
       //...
   }
   catch(FooException fe) {
       //throw new BarException("Error occured: "+fe.Message); //if you want to customize error message.
       throw; //If you don't need to change the message consider to not catch exception at all
   }
}

然后在业务逻辑之外显示消息:

void fooButton_Click(object sender, EventArgs args) {
   try {
        fooBusinessClass.foo();
   } catch(FooException fe) {
        //if(MessageBox.Show(fe.Message)==DialogResult.OK) fooBusinessClass.continuefoo(); //if you have several options
        MessageBox.Show(fe.Message);
   }
}

答案 1 :(得分:2)

我通常创建一个IView接口,然后将其注入业务逻辑类。如果逻辑“有问题”需要获得用户输入,那么它将如下所示:

interface IView
{
     bool AskUser(string question);
}

class SomeClass
{
     IView _View;

     public SomeClass(IView view)
     {
        _View = view;
     }

     public void SomeLogic()
     {
          if (someCondition && !_View.AskUser("continue?"))
               return;
     }
}

然后您的表单可以实现IView通过消息框提示询问用户。对于单元测试,您可以模拟静态设计案例中无法实际执行的视图。

答案 2 :(得分:2)

它比你想象的更容易在WinForms中实现一个简单的MVC-ish设计模式,你可以将其固定到现有代码上而无需进行重大修改。让表单或控件实现一个视图界面,​​并将视图传递给实现业务逻辑的类:

public interface IPersonDetailsView
{
    bool PromptUser(string message, string caption);
}
// Your form:
public partial class PersonDetailsForm : Form, IPersonDetailsView
{
    //...
    public bool PromptUser(string message, string caption) {
        var result = MessageBox.Show(message, caption, MessageBoxButtons.OkCancel);
        return result == DialogResult.Ok;
    }
}
// Your business logic:
public class PersonDetailsController {
    public IPersonDetailsView View { get; set; }

    public void DoingSomething() {
        // ...
        if (this.View.PromptUser(message, caption)) { ...
        }
    }
}

创建表单时将PersonDetailsController.View设置为表单。如果您需要表单能够与控制器对话,您只需添加PersonDetailsForm.Controller并让表单在控制器上调用公共方法。

我不会仅仅使用表单作为WinForms调用的代理,而是使用BDD方法,而不是View.ShowPrompt("Do you want to delete this person?", "Deleting person")我会选择像View.AskUserIfTheyWantToDeleteThePerson()这样的东西(没有参数)。这是一个很长的方法名称,但它非常明确,将实现和消息留给视图,从长远来看可以使事情更清晰。

答案 3 :(得分:1)

通常,业务层返回错误字符串。该字符串由GUI显示在MessageBox或状态栏中。

顺便说一句,您可以从消息框中获取结果(您不需要对话框):

MessageBoxResult MBR = MessageBox.Show("Click Me", "Title", MessageBoxButton.YesNoCancel);

MessageBox.Show("You selected: " + MBR.ToString());

答案 4 :(得分:1)

我会采用一种方法来使用事件来显示这种消息显示。然后,您可以通过订阅事件轻松决定是否要记录。

我将如何做到这一点:

首先为您的消息方法定义几个代理:

public delegate void DisplayMessage(string message);
public delegate bool DisplayDialogBox(string message, string caption);

这些可以用作业务逻辑类中的事件:

public class BusinessLogic
{
    public event DisplayMessage DisplayMessage;
    public event DisplayDialogBox DisplayDialogBox;

    protected void OnDisplayMessage(string message)
    {
        var dm = this.DisplayMessage;
        if (dm != null)
        {
            dm(message);
        }
    }

    protected bool OnDisplayDialogBox(string message, string caption)
    {
        var ddb = this.DisplayDialogBox;
        if (ddb != null)
        {
            return ddb(message, caption);
        }
        return false;
    }

    public void SomeMethod()
    {
        this.OnDisplayMessage("Hello, World!");
        var result = this.OnDisplayDialogBox("Yes or No?", "Choose");
        this.OnDisplayMessage(result.ToString());
    }
}

现在调用代码如下所示:

var bl = new BusinessLogic();

bl.DisplayMessage += MsgDisplay.DisplayMessage;
bl.DisplayDialogBox += MsgDisplay.DisplayDialogBox;

bl.SomeMethod();

这在我的测试中很有效。

现在,一个警告 - DisplayDialogBox委托返回bool所以当它被用作事件处理程序时,你可以让多个订阅者附加到事件,然后只返回最后一个返回值,但所有订阅者都将处理该事件。您可以弹出对话框,用户显示“否”,但下一个处理程序返回“是”,这就是返回的内容。

有一个相对容易的修复。将行return ddb(message, caption);替换为:

            return ddb
                .GetInvocationList()
                .Cast<DisplayDialogBox>()
                .Select(d => d(message, caption))
                .Aggregate((b1, b2) => b1 || b2);

只要您选择适当的聚合函数 - ||&& - 或按bool分组并选择计数最高的聚合函数 - 那么它就会很好用。

如果有帮助,请告诉我。