【问题标题】:C# - Determining responsibility between GUI and logic classC# - 确定 GUI 和逻辑类之间的责任
【发布时间】:2012-09-14 11:36:02
【问题描述】:

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

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 的东西。

处理此类情况的正确方法是什么。还是有第三种更好的实现方式。

【问题讨论】:

    标签: c# design-patterns user-interface


    【解决方案1】:

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

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

    这意味着UI层对逻辑层提供的类型/数据的依赖最小(只要它不知道逻辑是如何实现的就可以了),但是逻辑层有对 UI 实现是什么一无所知 - 这就是您想要的(系统中的较低级别组件不应该对较高级别的设计一无所知,而较高级别的组件必须对低级组件有基本的了解他们使用)。

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

    【讨论】:

      【解决方案2】:

      我建议在 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 击键等)引发所需的 Login 和 Cancel 事件。

      虽然这是一个简单的示例,但我做了很多 Windows Mobile 和 Windows CE 应用程序,在这些应用程序中,希望在外形尺寸迥异的操作系统变体上运行相同的应用程序是很常见的,这种方法可以让您真正掌握新的GUI 外形尺寸。这种用法的核心是动态加载的 UIFactory,以提供适当的 UI 实现。

      【讨论】:

        【解决方案3】:

        Logic 可以报告它支持的操作(第一种模式)在我看来很好(但 GetActions 的返回类型实际上应该是 IEnumerable&lt;string&gt; 而不是列表)。

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

        【讨论】:

          【解决方案4】:

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

          您可能想查看 MVC、MVP、MVVM 模式,所有这些模式目前都很流行。

          一般:

          尝试将逻辑与演示分开是个好主意,这样你就在正确的路线上。但是请记住,这种拆分的后果之一是,您的“逻辑”最好不要对演示文稿一无所知(因为您已经有一个负责该问题的类)。

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

          我发现一件好事是想象一下,您将使用表单前端和网络前端来开发这个应用程序,它们的功能完全相同。显然,由于所涉及的技术完全不同,这两个应用程序将具有完全不同的表示层。

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

          值得深思。

          【讨论】:

            【解决方案5】:

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

            【讨论】:

              【解决方案6】:

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

              【讨论】:

                猜你喜欢
                • 2013-05-28
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-02-11
                • 1970-01-01
                • 2023-03-06
                • 1970-01-01
                相关资源
                最近更新 更多