简体   繁体   English

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

[英]C#, separating message display code from the business logic

I have a winforms application and I'm not following any kind of design pattern. 我有一个winforms应用程序,我没有遵循任何设计模式。 My problem is, I have these base classes which contain all my business logic. 我的问题是,我有这些基类包含我的所有业务逻辑。 When ever an exception occurs or I need to display a dialog box to the user, I have written the code directly into the base classes where I need it. 当发生异常或我需要向用户显示对话框时,我已将代码直接写入我需要它的基类中。

I know that I need to separate my business logic and display logic, so I wrote a static class that includes the methods that I need to display the messages with. 我知道我需要将业务逻辑和显示逻辑分开,所以我编写了一个静态类,其中包含了显示消息所需的方法。

My question is, is there an easier way to separate business logic from the display? 我的问题是,是否有更简单的方法将业务逻辑与显示分开?

my static methods look like this, 我的静态方法看起来像这样,

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

So I'll be calling these methods from the base classes, like 所以我将从基类调用这些方法,比如

MsgDisplay.DisplayMessage(e.Message);

and is this method a good practice? 这种方法是一种很好的做法吗?

No, this method is not a good practice. 不,这种方法不是一个好习惯。 There is no any difference between your class and MessageBox class. 您的类和MessageBox类之间没有任何区别。
See, with your class: 看看你的班级:

MsgDisplay.DisplayMessage(e.Message);

and just using MessageBox 并只使用MessageBox

MessageBox.Show(e.Message);

This wrapper doesn't provide any additional functionality. 此包装器不提供任何其他功能。
If you want to separate business logic and ui, then you have to break down your methods and display message only in UI layer. 如果要分离业务逻辑和ui,则必须分解方法并仅在UI层中显示消息。
And, small point. 而且,小点。 Instead of: 代替:

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

type just: 输入:

return newresult==DialogResult.OK

UPDATE: If all you want to display is exception message then you should catch exception and show message on UI layer. 更新:如果您要显示的只是异常消息,那么您应该捕获异常并在UI层上显示消息。 So in your business class instead of displaying message: 所以在您的业务类中而不是显示消息:

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

throw exception to the ui layer: 将异常抛出到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
   }
}

and then display the message outside of the business logic: 然后在业务逻辑之外显示消息:

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

I typically create an IView interface which is then injected into the business logic classes. 我通常会创建一个IView接口,然后将其注入到业务逻辑类中。 If the logic "has a question" it needs to get user input it would then go like this: 如果逻辑“有问题”它需要获得用户输入,那么它将如下所示:

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

And then your form can implement IView to ask the user via message box prompt. 然后您的表单可以实现IView通过消息框提示询问用户。 For unit testing you can mock out the view which you can't really do in the static design case. 对于单元测试,您可以模拟静态设计案例中您无法实际执行的视图。

It's easier than you might think implementing a simple MVC-ish design pattern in WinForms, and you can bolt this onto existing code without significant modifications. 它比你想象在WinForms中实现一个简单的MVC-ish设计模式更容易,你可以将其固定到现有代码上而无需进行重大修改。 Have your form or control implement a view interface, and pass the view to the class that implements your business logic: 让您的表单或控件实现一个视图接口,并将视图传递给实现业务逻辑的类:

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)) { ...
        }
    }
}

Set the PersonDetailsController.View to the form when it is created. 在创建表单时将PersonDetailsController.View设置为表单。 If you need the form to be able to talk back to the controller you can just add a PersonDetailsForm.Controller and let the form call public methods on the controller. 如果您需要表单能够与控制器对话,您只需添加一个PersonDetailsForm.Controller并让表单在控制器上调用公共方法。

Rather than just using the form as a proxy for the WinForms calls I would use a BDD approach, so rather than View.ShowPrompt("Do you want to delete this person?", "Deleting person") I would go for something like View.AskUserIfTheyWantToDeleteThePerson() (with no arguments). 我不会仅仅使用表单作为WinForms调用的代理,而是使用BDD方法,而不是View.ShowPrompt("Do you want to delete this person?", "Deleting person")我会选择像View.AskUserIfTheyWantToDeleteThePerson()这样的东西View.AskUserIfTheyWantToDeleteThePerson() (没有参数)。 That's a long method name but it is very explicit, leaves the implementation and message up to the view and can make things clearer in the long run. 这是一个很长的方法名称,但它非常明确,将实现和消息留给视图,从长远来看可以使事情更清晰。

Generally, the business layer returns an error string. 通常,业务层返回错误字符串。 The string is displayed in a MessageBox or status bar by the GUI. 该字符串由GUI显示在MessageBox或状态栏中。

By the way, you can get a result from a message box (you don't need a dialog): 顺便说一句,您可以从消息框中获取结果(您不需要对话框):

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

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

I would take an approach of using events to surface this kind of message display. 我会采用一种方法来使用事件来表现这种消息显示。 You can then easily decide if you want to log or not by subscribing to the events. 然后,您可以通过订阅事件轻松决定是否要记录。

Here's how I would do it: 我是这样做的:

First define a couple of delegates for your message methods: 首先为您的消息方法定义几个委托:

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

These can be used as events in your business logic classes: 这些可以用作业务逻辑类中的事件:

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

Now the calling code looks like this: 现在调用代码如下所示:

var bl = new BusinessLogic();

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

bl.SomeMethod();

And this works nicely in my tests. 这在我的测试中很有效。

Now, one warning - the DisplayDialogBox delegate returns a bool so when it is used as an event handler you could have multiple subscribers attach to the event and then only the last return value is returned, but all of the subscribers will handle the event. 现在,一个警告 - DisplayDialogBox委托返回一个bool所以当它被用作事件处理程序时,你可以让多个订阅者附加到事件,然后只返回最后一个返回值,但所有订阅者都将处理该事件。 You could have the dialog box pop-up, the user says "No", but the next handler returns "Yes" and that's what's returned back. 您可以弹出对话框,用户显示“否”,但下一个处理程序返回“是”,这就是返回的内容。

There is a relatively easy fix. 有一个相对容易的解决方案。 Replace the line return ddb(message, caption); 替换行return ddb(message, caption); with: 有:

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

As long as you choose an appropriate aggregation function - || 只要您选择合适的聚合函数 - || , && - or group by the bool and choose the one with the highest count - then it'll work great. && - 或者由bool分组并选择计数最高的那个 - 然后它会很好用。

Let me know if this helps. 如果这有帮助,请告诉我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM