使用基于 UI 的程序结构是否成功?
您是否曾经根据用户界面部分构建源代码? 例如,如果您的 UI 包含:
- 用于显示某些属性的 GridView
- 3D 渲染面板
- 用于选择活动工具的
面板,则您或多或少地按以下方式命名和分组变量和函数:
class Application
{
string PropertiesPanel_dataFile;
DataSet PropertiesPanel_dataSet;
string[] PropertiesPanel_dataIdColumn;
void PropertiesPanel_OpenXml();
void PropertiesPanel_UpdateGridView();
string ThreeDView_modelFile;
Panel ThreeDView_panel;
PointF[] ThreeDView_objectLocations;
void ThreeDView_RenderView();
string ToolPanel_configurationFile;
Button[] ToolPanel_buttons;
void ToolPanel_CreateButtons();
}
您对此有何看法? 从长远来看,这种架构可以工作吗?
附言。 尽管这个解决方案可能会让您想起 Front-ahead-design april-fool 的笑话 http://thedailywtf .com/Articles/FrontAhead-Design.aspx 我的问题很严重。
编辑
维护和扩展此类代码已有半年了。 应用程序的主 .cs 文件中已增长到超过 3000 行,并且大约 2000 行分散到较小的文件中(包含通用帮助函数和类)。 代码的许多部分应该被概括并从主文件中取出,我一直在努力解决这个问题,但最终这并不重要。 代码的结构和细分非常简单,因此很容易浏览。 由于 UI 包含的主要组件少于 7 个,因此将整个设计一次性融入您的脑海中是没有问题的。 返回此代码(经过一段时间的休息)并立即知道从哪里开始总是令人愉快的。
我猜想这种巨大的类似过程的结构在我的例子中起作用的原因之一是 c# 中 UI 编程的类似事件的性质。 大多数情况下,这些代码所做的只是实现不同类型的事件,这些事件确实特定于该项目。 尽管某些事件函数立即变得长达几页长的怪物,但事件处理程序之间的耦合并不那么紧密,因此以后更容易重构和压缩它们。 这就是为什么我有意将泛化和重构留到以后,当其他项目开始需要与该项目使用的相同实现部分时。
PS 为了能够浏览 3000 行代码,我在 Visual Studio 中使用 FindNextSelection 和 FindPrevSelection 宏。 左键单击某个变量后,我按 F4 跳转到它的下一个实例,按 F2 跳转到上一个实例。 还可以选择变量名称的某些部分并在部分名称匹配之间跳转。 如果没有这些捷径,我很早就会迷路了:)
Have you ever structured your source code based on your user interface parts? For example if your UI consists of:
- GridView for showing some properties
- 3D rendering panel
- panel for choosing active tools
, then you name and group your variables and functions more or less in the following way:
class Application
{
string PropertiesPanel_dataFile;
DataSet PropertiesPanel_dataSet;
string[] PropertiesPanel_dataIdColumn;
void PropertiesPanel_OpenXml();
void PropertiesPanel_UpdateGridView();
string ThreeDView_modelFile;
Panel ThreeDView_panel;
PointF[] ThreeDView_objectLocations;
void ThreeDView_RenderView();
string ToolPanel_configurationFile;
Button[] ToolPanel_buttons;
void ToolPanel_CreateButtons();
}
What's your opinions on this? Can this architecture work in long run?
PS. Even though this solution might remind you of Front-ahead-design april-fool's joke http://thedailywtf.com/Articles/FrontAhead-Design.aspx my question is serious one.
EDIT
Have been maintaining and extending this kind of code for half a year now. Application has grown to over 3000 lines in the main .cs file, and about 2000 lines spread out in to smaller files (that contain generic-purpose helper-functions and classes). There are many parts of the code that should be generalized and taken out of the main file, and I'm constantly working on that, but in the end it doesn't really matter. The structure and subdivision of the code is so simple, that it's really easy to navigate though it. Since the UI contains less than 7 major components, there's no problem in fitting the whole design in you head at once. It's always pleasant to return to this code (after some break) and know immediately where to start from.
I guess one the reasons this gigantic procedural-like structure works in my case is the event-like nature of UI programming in c#. For the most part all this code does is implementation of different kinds of events, that are really specific to this project. Even though some event-functions immediately grow into couple of pages long monsters, coupling between event-handlers is not that tight, so it makes it easier to refactor and compress them afterwards. That's why Iam intentionally leaving generalization and refactoring for later time, when other projects start to require the same parts of implementation that this project uses.
PS to make it possible to navigate through 3000 lines of code I'm using FindNextSelection- and FindPrevSelection-macros in visual studio. After left-clicking on some variable I'm pressing F4 to jump to the next instance of it, and F2 to the previous instance. It's also possible to select some part of variable name and jump between partial-name matches. Without these shortcuts I would most defenetly lost my way long time ago :)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这在概念上看起来非常程序化,并且完全绕过了 OOD 的价值。 明智的方法是为每个元素创建对象,并且您给出的值将是这些对象的属性,即
我认为您已经明白了,所以我不会将所有内容都输入出来。 这是第一阶段,您可能还可以做进一步的工作来适当地构建您的应用程序。
我收到的关于 OOD 的最佳建议是寻找应用程序的每个逻辑分支可以提取到的最小对象,它可能具有属性的本机类型(对于 .NET,重新发明 Framework 对象也没有意义,因此它们可以在你的基类中),然后使用继承、多态性和封装来扩展这些基类,直到你有一个封装逻辑分支的对象。
当时我正在编写一个将数据推送到 I2C 设备的应用程序,因此我从一个将数据放入 I2C 总线的类开始,该类由将字节放入总线的类继承,该类由将字节放入总线的类继承。总线上的字节数组,最后是一个放置地址和字节数组的类。 这是相当极端的 OOD,但它生成了非常干净的代码,每个类都非常小并且非常容易调试。
预先考虑这个问题可能需要做更多的工作,但从长远来看,它节省了很多时间,这并不有趣。
That looks very procedural in concept and is completely bypassing the value of OOD. The sensible approach would be to create objects for each of your elements and the values you have given would be properties of those objects, i.e.
I think you get the idea so I'm not going to type the whole lot out. That's a first stage and there may be further work you could do to structure your application appropriately.
The best advice I ever received for OOD was to look to the smallest object that each logical branch of your app can be distilled to, it probably on has native types for properties (with .NET there no point in reinventing Framework objects either so they can be in your base class) and then using inheritance, polymorphism and encapsulation to expand on those base classes until you have an object that encapsulates the logical branch.
At the time I was writing an app that pushed data to an I2C device so I started with a class that put a bit onto an I2C bus, that was inherited by a class that put a byte onto a bus, inherited by a class that put an array of bytes onto the bus, and finally a class that put an address and an array of bytes. This is rather extreme OOD but it produced very clean code with each class being very small and very easy to debug.
It's possibly more work up front in thinking about the problem but in the long run it save soooooo much time it's just not funny.
根据 UI 部分构建用户界面代码是可以的,但程序的非 UI 相关逻辑应该保持分离。
但是在 UI 部分,您不应该只是将所有内容都粉碎到一个类中。 相反,您应该将 UI 代码划分为多个类,以便每个类仅处理一个 UI 组件,而不处理其他它不需要了解的组件:
It's OK to structure your user interface code based on your UI parts, but the non-UI related logic of your program should be kept separate.
But event on the UI part you shouldn't just smash everything into one class. Instead you should divide your UI code into several classes, so that every class only deals with one UI component and doesn't deal with others it doesn't need to know about:
一团糟。
不。(至少不是没有大量的汗水。)
这些名字非常冗长。 如果您考虑一下,长名称前缀是为了创建一种单独的“命名空间”,将相关的事物分组在一起。 对于这种事情已经有一个更好的语言结构——它就是类。 但主要问题在其他地方。
用户界面经常变化,概念很少变化。 如果您的代码结构反映了用户界面,那么您将被锁定到这个特定的界面。 这使得重用和重构代码变得非常困难。 如果您围绕问题领域的基本概念构建代码,那么您就有更好的机会在软件开发时重用现有代码——设计将适应变化。 而且变化总是发生在软件中。
(我希望“问题领域的基本概念”部分是清楚的。例如,如果您为当地剧院创建一个系统,您的设计应该基于电影、访客、座位等概念,而不是围绕 MovieList、SeatMap、TheaterPlan 等进行构建。)
大多数时候,尽可能将核心代码与 GUI 分离是一个好主意(这正是模型-视图-控制器设计系统的全部内容。)它不是学术练习,也不是仅在界面要更改时才需要。 将 GUI 与其余代码解耦的一个很好的例子是 Mac OS X 上的 GUI 编程,使用界面设计器、绑定等。 不幸的是,需要一段时间才能进入,您不能简单地浏览网络上的文档并得到启发。
It’s a mess.
No. (At least not without a lot of sweat.)
The names are insanely verbose. If you think about it, the long name prefixes are there to create a kind of separate ‘namespace’, to group related things together. There already is a better language construct for this very kind of thing – it’s classes. But the main problem is elsewhere.
User interfaces change often, concepts change seldom. If your code structure mirrors the user interface, you are locked to this particular interface. This makes reusing and refactoring the code quite hard. If you structure the code around the base concepts from the problem domain, you have a better chance to reuse already existing code as the software develops – the design will adapt to changes. And changes always happen in software.
(I hope that the ‘base concepts from the problem domain’ part is clear. For example, if you create a system for a local theater, you should base your design on the concepts of a Movie, Visitor, Seat, and so on, instead of structuring it around MovieList, SeatMap, TheaterPlan and such.)
Most of the time it is a good idea to decouple the core code from the GUI as much as possible (This is exactly what the Model–View–Controller design system is all about.) It is not an academic excercise, nor it is only required if the interface is going to change. A great example of decoupling the GUI from the rest of the code is the GUI programming on Mac OS X, with the Interface Designer, bindings and such. Unfortunately it takes a while to get into, you cannot simply skim the docs on the web and be enlightened.