工厂作为模块中的函数还是作为创建类的方法更符合Python风格?

4 投票
5 回答
4184 浏览
提问于 2025-04-11 09:17

我有一些Python代码,它可以根据从iCalendar文件解析出来的VEvent对象创建一个日历对象。

这个日历对象有一个方法,可以在解析事件时将这些事件添加进去。

现在我想创建一个工厂函数,这个函数可以从文件对象、路径或网址中创建一个日历。

我一直在使用iCalendar的Python模块,这个模块直接在返回实例的类上实现了一个工厂函数作为类的方法:

cal = icalendar.Calendar.from_string(data)

根据我对Java的了解,这在Java代码中是一个常见的模式,虽然我发现更多的情况是工厂方法在一个与实际想要实例化的类不同的类上。

我的问题是,这在Python中也算是“Pythonic”吗?还是说创建一个模块级别的方法作为工厂函数更符合Python的风格?

5 个回答

2

在我看来,模块级别的方法是一个更简洁的解决方案。它利用了Python的模块系统,这样可以给它一个独特的名字前缀,这正是“工厂模式”常用的地方。

6

在Python中,不用太纠结于你在某个地方看到的那些复杂的设计模式,比如工厂模式,想要到处使用它。

大多数情况下,如果你想到用@staticmethod来解决问题,其实用模块函数可能更好,除非你把多个类放在一个模块里,而且每个类对同一个接口有不同的实现,这时候用@staticmethod会更合适。

总的来说,无论你是通过@staticmethod还是模块函数来创建实例,差别不大。

我可能会更倾向于使用类的初始化方法(__init__),因为在Python中,一个比较常见的“模式”就是类的工厂就是类的初始化。

14

[注意:在讨论“日历”(一系列事件的集合)和“事件”(日历上的单个事件)时要非常小心。在你的问题中,似乎可能会有一些混淆。]

工厂设计模式有很多不同的变种。

  1. 一个独立的便利函数(例如,calendarMaker(data))

  2. 一个单独的类(例如,CalendarParser),它用来构建你的目标类(Calendar)。

  3. 一个类级别的方法(例如,Calendar.from_string)。

这些方法有不同的用途。它们都是符合Python风格的,关键问题是“你想表达什么?”和“什么可能会改变?”意思很重要,变化也很重要。

便利函数在Python中是很常见的。像Java这样的语言不能有独立的函数;你必须把一个孤立的函数放在一个类里面。Python允许你有一个独立的函数,而不需要类的负担。当你的构造函数没有状态变化、替代策略或对之前操作的记忆时,这个函数就很有用。

有时候,人们会定义一个类,然后提供一个便利函数,这个函数会创建这个类的实例,设置通常的状态和策略参数,以及其他配置,然后调用这个类的一个相关方法。这样你就同时拥有了类的状态性和独立函数的灵活性。

类级别的方法模式是可以使用的,但它有一些局限性。首先,它必须依赖于类级别的变量。由于这些变量可能会让人困惑,复杂的构造函数作为静态方法在需要添加功能(比如状态性或替代策略)时会遇到问题。确保你不会扩展这个静态方法。

其次,它与类的其他方法和属性几乎没有关系。像from_string这样的功能只是你日历对象的多种编码方式之一。你可能还会有from_xmlfrom_JSONfrom_YAML等等。这些都与日历本身是什么或它能做什么没有任何关系。这些方法主要是关于如何将日历编码以便传输。

在成熟的Python库中,你会看到工厂与它们创建的对象是分开的。编码(如字符串、XML、JSON、YAML)会经历很多随机的变化。然而,核心内容很少会改变。

将这两者分开。尽量把编码和表示与状态和行为保持距离。

撰写回答