c#,将消息显示代码与业务逻辑分离

本文关键字:业务 分离 代码 消息 显示 | 更新日期: 2023-09-27 18:08:59

我有一个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);

,这种方法是一个好的做法吗?

c#,将消息显示代码与业务逻辑分离

不,这种方法不是一种好的做法。你的类和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层显示消息。所以在你的业务类中不是显示message:

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);
   }
}

我通常创建一个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,通过消息框提示询问用户。对于单元测试,您可以模拟出视图,这在静态设计情况下是无法做到的。

在WinForms中实现一个简单的mvc设计模式比您想象的要容易,并且您可以将其绑定到现有代码中,而无需进行重大修改。让表单或控件实现视图接口,并将视图传递给实现业务逻辑的类:

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()(没有参数)。这是一个很长的方法名,但它非常明确,将实现和消息留给视图,从长远来看可以使事情更清晰。

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

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

MessageBoxResult MBR = MessageBox.Show("Click Me", "Title", MessageBoxButton.YesNoCancel);
MessageBox.Show("You selected: " + MBR.ToString());

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

我是这样做的:

首先为你的消息方法定义两个委托:

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,所以当它被用作事件处理程序时,您可以将多个订阅者附加到事件,然后只返回最后一个返回值,但所有订阅者将处理事件。您可以让对话框弹出,用户说"No",但是下一个处理程序返回"Yes",这就是返回的内容。

有一个相对简单的修复方法。将return ddb(message, caption);行替换为:

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

只要你选择一个合适的聚合函数- ||, && -或按bool分组并选择计数最高的一个-那么它就会工作得很好。

如果有帮助请告诉我