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);
,这种方法是一个好的做法吗?
不,这种方法不是一种好的做法。你的类和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
分组并选择计数最高的一个-那么它就会工作得很好。
如果有帮助请告诉我