简体   繁体   English

接近重构

[英]Approaching refactoring

I have a very data-centric application, written in Python / PyQt. 我有一个以数据为中心的应用程序,用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. 我打算做一些重构来真正将UI与核心分开,主要是因为还没有任何真正的测试,而且显然必须改变。

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. 当用户右键单击数据对象的表示时,弹出的上下文菜单由数据对象创建,尽管此数据对象(实质上是数据库行的ORM表示)应该与UI无关。

  • When something is written to the database, but the write fails (eg 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. 此对话框由数据提供程序*创建,但提供程序显然不应具有任何UI功能。 Clearly, the provider can instead raise an exception or otherwise indicate failure, and the UI can catch that and act accordingly. 显然,提供者可以提出异常或以其他方式指示失败,并且UI可以捕获并相应地采取行动。

    * 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 CRU D , 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 . 没有复杂的业务逻辑(它主要只是CRU D ,是的,即使没有D),这将比重写更重组,所以我并不真正关心引入这个问题中讨论的回归错误。

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. 我的计划是开始重构这个想法,因为UI部分可以很容易地被删除,例如,由Web前端或基于文本的界面而不是Qt界面替换。 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, eg data objects emit a changed signal to indicate, well, you know what. 另一方面,Qt本身仍将被核心使用,因为信号/插槽机制在很多地方使用,例如数据对象发出changed信号以指示,你知道什么。

So, my question: Is that a feasible approach to increase testability and maintainability? 所以,我的问题:这是一种提高可测试性和可维护性的可行方法吗? Any other remarks, especially with Python in mind? 还有其他任何评论,特别是考虑到Python吗?

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. 如果你还没有这样做,请阅读Michael Feathers的“有效使用遗留代码” - 它正是处理这种情况,并提供了丰富的技术来处理它。

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. 我相信Qt有自己的用于驱动GUI的测试框架,因此添加针对已知数据库操作GUI的测试并验证结果。 As you clean up the code you can replace or augment the end-to-end tests with unit tests. 在清理代码时,您可以使用单元测试替换或扩充端到端测试。

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 . 如果要从所有其他部分中提取应用程序的所有GUI部分以测试所有应用程序,则应使用Model-View-Presenter:您可以在此处此处找到一些解释。

With this model, all your services of your application uses the presenters whereas only the user can interact directly with the views (GUI parts). 使用此模型,应用程序的所有服务都使用演示者,而只有用户可以直接与视图(GUI部分)交互。 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. 在您想要修改GUI框架的情况下,您将拥有一个独立于您的应用程序的GUI部件。 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. 对于您想要的GUI测试,您只需为演示者编写单元测试 If you want to test GUI uses, you need to create integration tests . 如果要测试GUI使用,则需要创建集成测试

Hope that helps! 希望有所帮助!

I have done refactoring for large legacy code aiming UI/backend separation before. 我之前已经完成了针对UI /后端分离的大型遗留代码的重构。 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 . 无论模式是什么模式,还是MVC的一部分,拥有非常清晰的API层都是非常宝贵的。 If possible you may route all the UI requests through a dispatcher that would offer you greater control over UI<->Logic communication eg. 如果可能,您可以通过调度程序路由所有UI请求,该调度程序将为您提供对UI < - >逻辑通信的更大控制,例如。 implementing caching, auth etc. 实现缓存,身份验证等

To visualize: 想象:

[QT Frontend]
[CLIs]             <=======> [Dispatcher] <=> [API] <==> [Core/Model]
[SOAP/XMPRPC/Json]
[API Test Suite]

This way 这条路

  • it's easier to add test suite to test your APIs. 添加测试套件以测试API更容易。
  • Also it makes adding more UIs uniform way and easier. 此外,它还可以更加统一地添加更多UI。
  • API Documentation: Say if you want to document and expose APIs though some RPC interface, it' easier to generate API documentation. API文档:假设您希望通过某些RPC接口记录和公开API,则更容易生成API文档。 If somebody don't agree with importance of API documentation can always look at Twitter API and it's success. 如果有人不同意API文档的重要性,可以随时查看Twitter API并获得成功。
  • You can quickly import API layer to python shell and play with it 您可以快速将API层导入python shell并使用它

API designing can happen well before you start coding for API layer. 在开始编写API层之前,API设计可能会发生。 Depending on the application you might want to take help of packages like zinterfaces. 根据应用程序的不同,您可能希望获得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. 这种方法的一个明显优势是,在拥有API层和新UI之后,您现在可以回到遗留代码并以更小的步骤修复/重构它。

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? 请参阅interstar的建议, 了解在Brownfield应用程序中实现单元测试的首要任务是什么? .

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. 测试是为了确保您的重构确实完成了您打算做的事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM