接近重构
我有一个以数据为中心的应用程序,用 Python / PyQt 编写。我正在计划 进行一些重构以真正将 UI 与核心分离,主要是因为 目前还没有任何真正的测试,这显然必须改变。
已经有一些分离了,我想我已经以正确的方式做了很多事情,但还远非完美。两个例子,向您展示什么样的事情困扰着我:
当用户右键单击数据对象的表示时,上下文菜单 弹出的内容是由数据对象创建的,尽管该数据对象(本质上是 数据库行的 ORM 表示)显然应该与 UI 无关。
当向数据库写入数据但写入失败时(例如,因为数据库记录已被删除) 被不同的用户锁定),经典的“重试/中止”消息框呈现给用户。这 对话框由数据提供者* 创建,尽管提供者显然不应具有任何 UI 功能。 显然,提供者可以改为引发异常或以其他方式指示失败,并且 UI 可以捕获 并采取相应行动。
* 这是我用来表示基本上代表数据库表并调解的对象的词 其数据对象和数据库引擎之间;我不确定这是否是通常所说的 a "provider"
我没有测试经验,所以我不容易“感觉到”可测试性问题或类似问题,但在我进入这个问题之前,必须进行一些重组。
不涉及复杂的业务逻辑(主要只是 CRUD,是的,即使没有 D),而且这比重写要重组得多,所以我并不真正担心引入回归错误就像这个问题中讨论的那样。
我的计划是开始重构时考虑到 UI 部分可以很容易地被撕掉 取而代之的是 Web 前端或基于文本的界面,而不是 Qt 界面。另一方面, Qt本身仍然会被核心使用,因为信号/槽机制在很多地方使用, 例如,数据对象发出一个changed
信号来指示,嗯,你知道吗。
所以,我的问题是:这是提高可测试性和可维护性的可行方法吗?还有其他评论吗,尤其是考虑到 Python 的时候?
I have a very data-centric application, written in Python / PyQt. I'm planning
to do some refactoring to really separate the UI from the core, mainly because
there aren't any real tests in place yet, and that clearly has to change.
There is some separation already, and I think I've done quite a few things the right way, but it's far from perfect. Two examples, to show you what kind of things are bothering me:
When the user right-clicks on the representation of a data object, the context menu
that pops up is created by the data object, although this data object (essentially the
ORM representation of a database row) should clearly have nothing to do with the UI.When something is written to the database, but the write fails (e.g. because the database record is
locked by a different user), the classical "retry / abort" message box is presented to the user. This
dialog is created by the data provider*, although the provider should obviously not have any UI functionality.
Clearly, the provider can instead raise an exception or otherwise indicate failure, and the UI can catch
that and act accordingly.* that's the word I use for the object that basically represents a database table and mediates
between its data objects and the database engine; I'm not sure whether that's what is usually called
a "provider"
I don't have experience with testing, so I don't easily "feel" testability problems or the like, but before I get into that, some reorganizing has to be done.
There is no complicated business logic involved (it's mainly just CRUD, yes, even without the D), and this would be much more reorganizing than rewriting, so I'm not really concerned about introducing regression bugs like discussed in this question.
My plan is to start refactoring with the idea in mind that the UI part could easily be ripped out to be
replaced by, say, a web frontend or a text-based interface instead of the Qt interface. On the other hand,
Qt itself would still be used by the core, because the signal/slot mechanism is used in quite a few places,
e.g. data objects emit a changed
signal to indicate, well, you know what.
So, my question: Is that a feasible approach to increase testability and maintainability? Any other remarks, especially with Python in mind?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
如果您还没有这样做,请阅读 Michael Feathers 的《有效处理遗留代码》——它正是处理这种情况,并提供了大量的技术来处理它。
他提出的一个关键点是在重构之前尝试进行一些测试。由于它不适合单元测试,因此尝试进行一些端到端测试。我相信 Qt 有自己的测试框架来驱动 GUI,因此添加针对已知数据库操作 GUI 的测试并验证结果。当您清理代码时,您可以用单元测试替换或增强端到端测试。
If you have not done so already, read "Working Effectively with Legacy Code" by Michael Feathers - it deals with exactly this sort of situation, and offers a wealth of techniques for dealing with it.
One key point he makes is to try and get some tests in place before refactoring. Since it is not suitable for unit tests, try to get some end-to-end tests in place. I believe that Qt has its own testing framework for driving the GUI, so add tests that manipulate the GUI against a known database and verifies the result. As you clean up the code you can replace or augment the end-to-end tests with unit tests.
如果您想从应用程序的所有其他部分中提取所有 GUI 部分以测试所有应用程序,您应该使用 Model-View-Presenter:您可以找到一些解释 此处 和 此处。
使用此模型,应用程序的所有服务都使用演示者,而只有用户可以直接与视图(GUI 部分)交互。演示者正在管理应用程序中的视图。如果您想要修改 GUI 框架,您将拥有独立于应用程序的 GUI 部分。您唯一需要修改的是演示者和视图本身。
对于您想要的 GUI 测试,您只需为演示者编写单元测试。如果您想测试 GUI 使用,则需要创建集成测试。
希望有帮助!
If you want to extract all GUI parts of your application from all the other parts in order to tests all your application, you should use the Model-View-Presenter: You can find some explanation here and here.
With this model, all your services of your application uses the presenters whereas only the user can interact directly with the views (GUI parts). The presenters are managing the views from the application. You will have a GUI part independent from your application in the case you want to modify the GUI framework. The only thing you will have to modify are the presenters and the views themselves.
For the GUI tests you want, you just have to write unit tests for presenters. If you want to test GUI uses, you need to create integration tests.
Hope that helps!
我之前曾对大型遗留代码进行过重构,旨在实现 UI/后端分离。这既有趣又有益。
/赞美;)
无论人们如何称呼它,或者它是 MVC 的一部分,拥有一个非常清晰的 API 层 都是非常宝贵的。如果可能的话,您可以通过调度程序路由所有 UI 请求,这将为您提供对 UI<->逻辑通信的更大控制,例如。实现缓存、身份验证等。
可视化:
这样
如果有人不同意 API 文档的重要性,可以随时查看 Twitter API,它是成功的。
API 设计可以在开始为 API 层编码之前进行。根据应用程序,您可能需要使用 zinterfaces 等软件包的帮助。
这是我在编写非常小的应用程序时所采取的通用方法,而且它对我来说从未失败过。
请查看
这种方法的一个显着优势是在拥有 API 层和新 UI 后,您现在可以返回旧代码并修复/以较小的步骤重构它。
其他建议是准备好您的测试套件。请参阅 interstar 的建议,网址为 什么是第一个在棕地应用程序中实施单元测试的任务?。
I have done refactoring for large legacy code aiming UI/backend separation before. It's fun and rewarding.
/praise ;)
Whatever pattern one calls it or be it part of MVC it's invaluable to have a very clear API layer. If possible you may route all the UI requests through a dispatcher that would offer you greater control over UI<->Logic communication eg. implementing caching, auth etc.
To visualize:
This way
If somebody don't agree with importance of API documentation can always look at Twitter API and it's success.
API designing can happen well before you start coding for API layer. Depending on the application you might want to take help of packages like zinterfaces.
This is general approach I take even while writing very small apps and it has never failed for me.
Do look at
One distinct advantage of this approach is after you have API layer and new UI, you can now go back to legacy code and fix/refactor it in perhaps smaller steps.
Other suggestions is to have your testing suite ready. See interstar's advice at What are the first tasks for implementing Unit Testing in Brownfield Applications? .
有一点目前尚未提及,也没有真正回答问题,但非常重要:只要有可能,您就应该在开始重构之前立即进行测试。测试的要点是检测你是否破坏了某些东西。
重构对于准确了解某些操作的效果发生变化以及相同的调用在何处产生不同的结果非常有价值。这就是所有这些测试的目的:您想看看是否破坏了某些东西,您想看到所有无意的更改。
因此,现在对重构后仍应产生相同结果的所有部分进行测试。测试并不是针对永远保持不变的完美代码,测试是针对需要更改的代码、需要修改的代码、将要重构的代码。测试是为了确保您的重构确实达到了您的预期目的。
One point, not mentioned so far and not really answering the question, but very important: Wherever possible you should put in the test now, before you start refactoring. The main point of test is that to detect if you break something.
Refactoring is something where it is really valuable to exactly see where the effect of some operation changed and where the same call produces a different result. That's what all this testing is about: You want to see if you break something, you want to see all the unintentional changes.
So, make tests now for all the parts that still should produce the same results after refactoring. Tests aren't for perfect code that will stay the same forever, tests are for the code that needs to change, the code that needs to be modified, the code that will be refactored. Tests are there to make sure your refactoring really does what you intend it to do.