重构的方式

7 投票
4 回答
773 浏览
提问于 2025-04-15 18:07

我有一个以数据为中心的应用程序,是用Python和PyQt写的。我打算进行一些重构,真正把用户界面(UI)和核心部分分开,主要是因为现在还没有真正的测试,这显然需要改变。

现在已经有一些分离的做法,我觉得我做了一些正确的事情,但远远不够完美。这里有两个例子,来说明我在意的地方:

  • 当用户右键点击一个数据对象的表示时,弹出的上下文菜单是由这个数据对象创建的,尽管这个数据对象(基本上是数据库一行的ORM表示)显然和用户界面没有关系。

  • 当写入数据库的操作失败时(比如因为数据库记录被其他用户锁定),会弹出经典的“重试/放弃”消息框给用户。这个对话框是由数据提供者创建的,尽管提供者显然不应该有任何用户界面功能。实际上,提供者可以抛出一个异常或者以其他方式表示失败,然后用户界面可以捕捉到这个信息并做出相应的处理。

    * 我用“提供者”这个词来指代基本上代表数据库表的对象,它在数据对象和数据库引擎之间进行调解;我不确定这是否是通常所说的“提供者”。

我没有测试方面的经验,所以不容易“感觉到”可测试性的问题,但在我开始进行测试之前,必须先进行一些重组。

这里没有复杂的业务逻辑(主要就是增、查、改,没错,连删都没有),而且这将更多是重组而不是重写,所以我并不太担心会引入回归错误,就像在这个问题中讨论的那样。

我的计划是开始重构,考虑到用户界面部分可以很容易地被替换成,比如说,一个网页前端或者文本界面,而不是Qt界面。另一方面,Qt本身仍然会被核心部分使用,因为在很多地方使用了信号/槽机制,比如数据对象会发出一个changed信号来表示,嗯,你知道的。

所以,我的问题是:这样的做法是否可行,以提高可测试性和可维护性?还有其他的建议吗,特别是考虑到Python的情况?

4 个回答

2

如果你想把应用程序的所有界面部分和其他部分分开,以便对整个应用进行测试,你可以使用“模型-视图-主持人”这种设计模式。你可以在这里这里找到一些解释。

在这个模型中,应用程序的所有服务都通过主持人来使用,而只有用户可以直接与界面部分(也就是图形用户界面)进行互动。主持人负责管理应用程序的视图。这样,如果你想修改界面框架,界面部分就可以和应用程序独立开来。你只需要修改主持人和视图本身。

对于你想要的界面测试,你只需要为主持人编写单元测试。如果你想测试界面的使用情况,就需要创建集成测试

希望这对你有帮助!

7

如果你还没看过,建议你读一下迈克尔·费瑟斯的《有效处理遗留代码》。这本书正好讲了这种情况,并提供了很多处理这类问题的技巧。

他提到的一个关键点是,在你开始重构代码之前,尽量先写一些测试。因为这段代码不适合做单元测试,所以可以尝试写一些端到端的测试。我记得Qt有自己的测试框架,可以用来测试图形界面,所以你可以写一些测试,去操作图形界面,使用一个已知的数据库,并验证结果。随着你逐步清理代码,你可以用单元测试来替代或补充这些端到端的测试。

1

我之前做过大型旧代码的重构,目标是把用户界面和后台分开。这过程既有趣又有成就感。

/赞 ;)

不管你怎么称呼这种模式,或者它是不是MVC的一部分,拥有一个非常清晰的API层是非常重要的。如果可能的话,你可以通过一个调度器来处理所有的用户界面请求,这样你就能更好地控制用户界面和逻辑之间的沟通,比如实现缓存、身份验证等功能。

为了更好地理解:

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

这样做的话

  • 更容易添加测试套件来测试你的API。
  • 也让添加更多用户界面变得统一且简单。
  • API文档:比如说你想通过某个RPC接口来记录和展示API,生成API文档会更简单。如果有人不认同API文档的重要性,可以看看Twitter的API和它的成功。
  • 你可以快速把API层导入到Python环境中,进行尝试。

API设计可以在你开始编写API层代码之前就进行。根据应用的不同,你可能会想使用像zinterfaces这样的工具包。这是我在写非常小的应用时也会采用的一种通用方法,而且从来没有失败过。

建议你看看

这种方法的一个明显优势是,当你有了API层和新的用户界面后,你可以回过头去修复或重构旧代码,可能会以更小的步骤进行。

另一个建议是准备好你的测试套件。可以参考interstar在在旧应用中实施单元测试的第一步任务中的建议。

撰写回答