C# 中的 UI 和逻辑分离
有人对将逻辑排除在我的 GUI 类之外有什么建议吗?我尝试使用良好的类设计并尽可能保持分离,但我的 Form 类通常最终会混合比我想要的更多的非 UI 内容,这往往会使维护变得非常痛苦。
(Visual Studio 2008 Professional、C#、Windows 应用程序)。
非常感谢。
Does anyone have any advice on keeping logic out of my GUI classes? I try to use good class design and keep as much separated as possible, but my Form classes usually ends up with more non-UI stuff mixed in than I'd like, and it tends to make maintenance a real pain.
(Visual Studio 2008 Professional, C#, Windows apps).
Many thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
将您的逻辑放在单独的程序集中;并且,构建该程序集而不引用任何 GUI 包(例如 System.Drawing、System.Windows.Forms 等)。
Put your logic in a separate assembly; and, build that assembly without its referencing any GUI packages (e.g.
System.Drawing
,System.Windows.Forms
, etc.).这实际上只是一个练习和自律的问题。我的意思是,我们都做到了。我们都会时不时地在错误的条件下这样做(经理/客户大喊着“现在”完成某件事,而不是“正确”完成某件事,等等)。
在编写驱动 UI 的代码时(更多的是在 Web 端,但同样的事情也适用),我做的一件事是问自己每个代码单元(单行、条件、循环等)是否该部分代码的数量取决于 UI 的存在。如果我正在向文本框写入内容,则该内容取决于 UI,因此会写入文本框。但如果我正在计算该文本框中的结果,那可能就是业务逻辑。
另一种方法(正如 ChrisW 在我打字时提到的那样)是首先在非 UI 类库中开发逻辑。尽可能多地放入不依赖于基于 UI 的库的逻辑(尽管使用您的判断来定义“逻辑”)。然后构建 UI 以利用该逻辑。有不同的方法可以允许这两个部分的并发开发,例如删除接口类后面的逻辑程序集,只将 UI 部分编码到这些接口(然后使用依赖项注入将程序集类插入到接口中)等。
It's really just a matter of practice and self discipline. I mean, we've all done it. And we all continue to do it from time to time under the wrong conditions (manager/customer yelling to get something done "right now" vs. "right", etc.).
One thing I do when writing code to drive the UI (more on the web side, but the same thing applies) is to ask myself with each unit of code (a single line, a conditional, a loop, etc.) whether that piece of code depends on the presence of the UI. If I'm writing to a text box, that's UI-dependent, so it goes there. But if I'm calculating the result that will go in that text box, that's probably business logic.
Another approach (as ChrisW alluded to out while I'm typing) is to develop the logic first in a non-UI class library. Put as much logic in there as you can (use your judgment as to what defines "logic" though) that doesn't depend on UI-based libraries. Then build the UI to make use of that logic. There are different approaches to allow the concurrent development of these two pieces, such as stubbing out the logic assembly behind interface classes and just coding the UI pieces to those interfaces (then using dependency injection to plug the assembly classes into the interfaces), etc.
3 层架构正是您所寻找的。
您构建 2 个可重用层:
仅包含所需的代码
从数据库
消耗DAL,包含业务
规则,验证,并提供
UI 的外观
然后在您的 UI 项目中引用可重用层并仅处理 UI 特定的内容。 UI 项目仅与 BLL 通信,与 DAL 没有直接连接:
如果您想支持多种数据库类型,您可以拥有多个使用可重用组件的 UI 层,以及多个可互换的 DAL。
The 3 layer architecture is what you are looking for.
You build 2 reusable layers:
contains only the code needed to
read/write from the database
consumes the DAL, contains business
rules, validation, and provides a
facade for the UI to use
Then on your UI project your reference the reusable layers and handle only the UI specific stuff. The UI project talks only to the BLL, with no direct connection to the DAL:
You can have multiple UI layers that consume your reusable components, as well as multiple interchangable DALs if you want to support multiple database types.
您需要研究设计模式,例如:
Model-View-网站 (ASP.NET) 经常使用的控制器 (MVC)
WPF 经常使用的 模型-视图-视图模型 (MVVM)
通过保持其中之一,您应该能够将应用程序的各个部分分开。
还有其他模式可以完成类似的工作。
此外,使用 WPF 进行开发也很有帮助,因为 UI 是由 XAML 定义的,而完成工作的代码是 C#。这可以提供基本程度的分离。如果您发现自己编写的 C# 代码只是操作 UI,您可以退一步思考“我应该在 XAML 中执行此操作吗?”。显然,您可能需要在后面的代码中做一些事情,但这是一个开始。
You need to look into design patterns like:
Model-View-Controller (MVC) often used by web sites (ASP.NET)
Model-View-View Model (MVVM) often used by WPF
By keeping to one of these you should be able to keep the various parts of your application separate.
There are other patterns that do a similar job.
Also, developing with WPF can help as the UI is defined by the XAML and the code that does the work is the C#. This can provide a basic degree of separation. If you find yourself writing C# code that just manipulates the UI you can take a step back and think "should I do this in XAML?". Obviously there might be things you have to do in the code behind, but it's a start.
了解如何编写可将数据绑定到表单的控制器类以及如何执行数据绑定。
在 WinForms 中,这主要归结为控制器类上的 INotifyPropertyChanged 和 IDataErrorInfo 接口以及在表单类上使用 BindingSource 实例。
然后,您可以编写一个包含 UI 的所有数据和逻辑的控制器类,并且 UI 类只需绑定到它即可。然后,您的 UI 类会变得非常薄,并且您的 UI 逻辑(保存在控制器中)变得非常可测试(针对 UI 类运行时单元测试很棘手,但针对控制器类运行时要容易得多)。
这是所有 MVC/MVVM 设计的基础。
赫比
Learn how to write controller classes that can be databound to the form and how to perform the databinding.
In WinForms this mainly comes down to INotifyPropertyChanged and IDataErrorInfo interfaces on the controller class and using BindingSource instances on the form class.
Then you can write a controller class that contains ALL the data and logic for the UI and the UI class simply binds to it. Your UI class then becomes very thin, and your UI logic (held in the controller) becomes very testable (unit tests are tricky when running against UI classes, but much easier when running against controller classes).
This is the basis of all the MVC/MVVM designs.
Herbie
通常在这样的情况下;我创建了一个辅助方法类来完成所有繁重的工作。
至于保持逻辑分离;确定关键组件是什么,并将它们重构到辅助方法类中。例如;如果我正在处理 GridView 以根据是否选择记录来更新记录;如果是,则更新表单中的 ShipDate;我首先要弄清楚该行是否被选中;然后提取 Id 字段,然后提取 ShipDate,然后将 Id 和 ShipDate 传递到我的帮助器类上完成所有工作的方法中。
单元测试可以成为你的朋友;基本上,如果您有任何执行“逻辑”类型的代码;它应该有一个单元测试。如果它在 GUI 类中;很难测试;然而一旦你重构它;单元测试应该是微不足道的。
Typically in situations like this; I create a helper method class that does all the heavy lifting.
As to keeping the logic separate; identify what are the key components, and refactor those out into that helper method class. For example; if I'm processing a GridView to update records based on whether or not they're selected; and if they are, update the ShipDate, in the form; i'd figure out first if the row is selected; then extract the Id field, then the ShipDate, and then pass the Id and ShipDate into a method on my helper class that does all the work.
Unit tests can be your friend here; basically, if you have any code that does "logic" type stuff; it should have a unit test. If it's in the GUI classes; it's difficult to test; however once you refactor it out; the unit test should be trivial.
您应该查看以下模式:
MVC(模型-视图-控制器)
MVVM(模型-视图-视图-模型)- 主要用于 WPF 中,具有丰富的数据绑定支持。
MVP(模型-视图-演示者)- 通常用于 WinForms 和 Web 应用程序(因为无状态视图)
查看这篇博客文章,其中给出了如何使用 MVP 通过一个演示者来支持 Web 和 WinForms 视图的示例:
http://www.cerquit.com/blogs/post/MVP-Part-I-e28093-Building-it-from-Scratch.aspx" cerquit.com/blogs/post/MVP-Part-I-e28093-Building-it-from-Scratch.aspx
此外,此处的另一篇博客文章描述了使用 MVP 模式对业务逻辑进行单元测试:
http://www.cerquit.com/blogs/post/Model-View-Presenter-Part-II---Unit-Testing.aspx" cerquit.com/blogs/post/Model-View-Presenter-Part-II---Unit-Testing.aspx
You should look at the following patterns:
MVC (Model-View-Controller)
MVVM (Model-View-View-Model) - Mostly used in WPF with it's rich databinding support.
MVP (Model-View-Presenter) - Often used for WinForms and web apps (because of the stateless view)
Check out this blog post which gives an example of how to use MVP to power both a web and WinForms view with one presenter:
http://www.cerquit.com/blogs/post/MVP-Part-I-e28093-Building-it-from-Scratch.aspx
Also, a further blog post here describes using the MVP pattern for unit-testing your business logic:
http://www.cerquit.com/blogs/post/Model-View-Presenter-Part-II---Unit-Testing.aspx
总之,这就是重构。
将代码放入 UI 中的原因只有几个:
放置在业务逻辑层但是
我通常在中添加一个辅助方法
UI(更容易)
所有其他“业务逻辑”代码都进入另一个称为业务逻辑类的类。所有数据库交互代码都进入一个不同的类,称为数据访问类。
当您在 UI 中编写代码时,只需问问自己代码是否与表单上的控件进行交互。如果不是,它可能属于其他两类。
查看 Martin Fowler 撰写的一些有关重构的书籍,例如“重构:改进现有代码的设计”。另一个流行词是关注点分离。我知道您可以在一个类中完成所有这些工作,但是当代码按照我上面的描述分成类时,代码将变得更具可读性并且更易于调试。
In a word, it is called Refactoring.
There are only a couple of reasons to put code into the UI:
placed in a business logic layer but
I usually add a helper method in the
UI (much easier)
All the other "Business Logic" code goes into another class called the business logic class. All the Database interaction code goes into a different class called the data access class.
When you write the code in the UI, simply ask yourself if the code is interacting with a control on the form. If not, it probably belongs in the other two classes.
Check out some books by Martin Fowler on refactoring like "Refactoring: Improving the Design of Existing Code". Another buzz word is seperation of concerns. I know you can do all this in one class but code becomes much more readable and easier to debug when it is seperated into classes as I described above.