C# - 确定GUI和逻辑类之间的责任

时间:2012-09-14 11:36:03

标签: c# design-patterns user-interface

我有一个表单和一个逻辑类。根据用户操作,该类生成一系列操作。然后,这些操作需要在表单上显示为按钮,以便用户可以从中进行选择。 我最初的解决方案是:

public class Logic {
    public List<string> GetActions() {
        List<string> result = new List<string>();
        // ...prepare list
        return result;
    }
}

public class FrmGUI : Form {
    Logic logic = new Logic();
    private void PopulateButtons() {
        foreach(string action in logic.GetActions(){
            //...create button
        }
    }
}

GUI从Logic类中检索字符串列表,然后使用它来使用按钮填充面板。现在据说这是糟糕的OO练习,因为我暴露了一些关于Logic类行为的东西。这里假设GetActions方法将始终存在,并且Logic类将始终能够返回此字符串列表。

另一种解决方案是:

public class Logic {
    public void PopulateButtons(Panel panel, Action<object, EventArgs> eventHandler) {
        // ...prepare list
        // ...populate buttons
    }
}

public class FrmGUI : Form {
    Logic logic = new Logic();
    private void PopulateButtons() {
        logic.PopulateButtons(this.panel1, actionButtonClickHandler);
    }
}

现在,GUI类对逻辑类一无所知,只希望填充按钮。另一方面,逻辑类现在涉及GUI的东西。

处理此类案件的正确方法是什么。或者是第三种更好的实施方式。

6 个答案:

答案 0 :(得分:2)

我使用前一个模式:逻辑层创建信息,UI层使用该信息创建UI。

这样,如果您决定重新设置UI以使用项目的下拉列表,则只需更改UI层,而不是逻辑。

这意味着UI层对逻辑层提供的类型/数据的依赖性最小(只要它不知道如何实现逻辑,那很好),但是逻辑层有不知道UI实现是什么 - 这是你想要的(系统中的低级组件不应该对高级设计有任何了解,而更高级别的组件必须对低级组件有基本的了解)他们利用)。

最好是应用程序(或其他一些外部实体)创建逻辑和UI并将它们链接在一起,而不是创建逻辑的UI本身 - 这将有助于UI和逻辑更松散地耦合

答案 1 :(得分:1)

Logic可以报告它支持的操作(第一种模式)对我来说很好(但GetActions的返回类型确实应该是IEnumerable<string>而不是列表。)

不太好的是,在您的示例中,表单直接实例化Logic类。通常,您可以为可能具有的不同类型的Logic类创建接口或抽象基类,并且具体实现填充功能。然后,表单将通过某种inversion-of-control机制获得逻辑。

答案 2 :(得分:1)

正确?????多年来,很多人投入了大量时间来尝试标准化这种方法,我担心答案可能会从ui设计模式的数量中推断出来!

你可能想看一下MVC,MVP,MVVM模式,所有这些模式目前都很流行。

一般来说:

尝试从演示中分离逻辑是个好主意,所以你就是在正确的路线上。但请记住,这种分裂的后果之一是,你的“逻辑”更好地不了解任何关于演示的内容(因为你已经有一个类负责这个)。

所以你可能想要考虑“按钮”的概念,并且(从逻辑的角度来看),“我真的不是指命令吗?”。当你在屏幕的背景下想到它们时,它们才真正成为按钮。但是,比方说,在特定银行账户上加载交易的命令......你不需要一个屏幕来概念化它是如何工作的。

我发现的一件好事是想象你将开发这个应用程序同时使用表单前端,比如说,一个Web前端完全相同的东西。显然,由于所涉及的技术根本不同,这两个应用程序将具有完全不同的表示层。

但是因为你不想两次编写代码,所以你也会有一个“逻辑”层,你可以在那里填充尽可能多的常用代码。例如,决定银行账户是否透支 - 无论您是网络还是赢取,透支仍然是透支。相反,任何你最终在web和win之间编写不同代码的地方都属于你的“演示”层。例如,以红色显示透支余额。

深思熟虑。

答案 3 :(得分:1)

我建议在LogicFrmGUI之间放一层抽象。

举一个简单的例子,假设你有一个登录你的应用程序。为逻辑屏幕定义界面。注意,这里没有提到使用什么控件。 Logic类永远不会知道使用的UI类/表单。

interface ILoginScreen : IScreen
{
   event EventHandler LoginInvoked;
   event EventHandler CancelInvoked;

   string User { get; set; }
   string Password { get; set; }   
}

在您的LoginLogic类中,您有以下代码:

void Start() // initial LoginLogic method
{

   ILoginScreen loginScreen = uiFactory.CreateLoginScreen();

   loginScreen.User = string.empty;
   loginScreen.Password = string.empty;

   loginScreen.LoginInvoked += new EventHandler(LoginScreen_LoginInvoked);
   loginScreen.CancelInvoked += new EventHandler(LoginScreen_CancelInvoked);

   loginScreen.Show();

}

void LoginScreen_LoginInvoked(s, e)
{
   if (ValidateCredentials(loginScreen.User, loginScreen.Password))
   {
      // goto the next screen logic controller
   }
}

在表单中,您实现ILoginScreen并使用来自USer和Password属性的数据刷新UI字段。此外,您可以根据用户反馈(按钮单击,Escape击键,等等)提高所需的登录和取消事件。

虽然这是一个简单的例子,但我做了很多Windows Mobile和Windows CE应用程序,在这些应用程序中,想要在截然不同的外形操作系统变体上运行相同的应用程序是很常见的,这种方法可以让你真正抓住新的GUI形式因素。这种用法的核心是动态加载的UIFactory,以提供适当的UI实现。

答案 4 :(得分:0)

第一个更好,因为GUI和逻辑之间的接口只是一个字符串列表。 之后,这完全取决于您从按钮调用逻辑类的操作的方式。 如果你有一个采用动作字符串的泛型方法,那很好。如果需要根据操作字符串在逻辑类上调用不同的方法,则需要在GUI类中映射以映射操作字符串和方法调用。你也可以从逻辑类中导入这个“动作字符串 - 映射方法”,以保持分离。

答案 5 :(得分:0)

我的观点是,这取决于创建类似逻辑层和GUI层的原因。我认为最常见的原因是重用逻辑,例如将其用于WPF和Web GUI,或者在将数据发送到GUI之前必须处理数据。你的第一个例子符合上述模式。在你的第二个例子中,逻辑似乎不可重用,因为它的gui具体。 但是,在现实世界中有正确或错误的答案。该体系结构应该满足您的需求并使您的项目可维护(例如,通过减少冗余代码)。 在您的情况下,问题是:您多久需要这些功能以及何时/何时需要它们?