近期想把自己所做的任务的共用部分抽象出来,做成通用的基类。这样就能把不同数据集,不同模型集成到一个统一的框架中,提高开发效率,规范开发标准,提高代码可读性。
但是在设计其中一个数据集基类时遇到一个问题。
由于所有的数据集类都符合下面的流程:解析标注文件->解析数据文件。用户需要手动控制解析数据文件的时机,因此我想把解析标注文件的工作放到基类中。即基类有一个抽象方法parse_ann_file()
,并会在类被构造时自动调用,子类只需要重载这个函数就行了。
但问题在于,不同的数据集解析文件需要的参数是不一样的。如A数据集可能需要一个文件名,而B可能需要三个文件名。于是我想到可以在子类的构造函数里传入解析需要的参数,保存为成员变量,再在该子类的parse_ann_file()
中直接使用。
但由于我们的构造函数调用了parse_ann_file()
,因此对super(ClassName).__init__()
的调用必须放在初始化成员变量之后。限制super
的位置始终让我觉得怪怪的。但是由于这样工作正常了,我就这样写了。
后来查到一个帖子Stackoverflow遇到了和我一样的问题。才发现这是一种非常糟糕的设计。如果这是C++,我们甚至无法决定__init__
调用的时机。
于是我重新审视了自己当时自作聪明的写法。事实上每个子类只少写了一行,却因此搞乱了代码逻辑,增加了代码的相互关联,降低了代码的可维护性。因此不如把这个抽象函数取消,让每个子类自己决定解析的时机。
可能不少人都和我一样是半路出家的程序员,写代码时按自己的理解过分追求结构和设计模式,最终却被证明是画蛇添足。追求设计没有错,但是要有扎实的理论支撑才行,不要盲目增加无用的代码。