You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

仅需函数式行为时,使用面向对象风格接口与类是否恰当?

当仅需函数式行为时,使用面向对象风格的接口与类是否恰当?

当我们只需要函数式行为(无需跟踪或修改状态,实例仅用于调用唯一暴露的方法一次)时,用面向对象风格的接口和类来实现是否合理?

我经常写出这样的Python代码:

from abc import ABC, abstractmethod


class IFoo(ABC):
    @abstractmethod
    def bar(self) -> str:
        ...


class ThisFoo(IFoo):
    def __init__(self, x: str):
        self._x = x

    def bar(self) -> str:
        return f'Doing this with {self._x}'


class ThatFoo(IFoo):
    def __init__(self, x: str):
        self._x = x

    def bar(self) -> str:
        return f'Doing that with {self._x}'

这些Foo类的唯一用途就是让外部调用其bar方法,而且客户端代码只会调用一次。我通常会写一个便捷函数来封装这个逻辑:

def bar(type_: str, x: str) -> str:
    dispatcher = {
        'this': ThisFoo,
        'that': ThatFoo
    }
    Foo = dispatcher[type_]
    foo = Foo(x)
    return foo.bar()

客户端代码可以这样调用:

print(bar('this', 'baz'))
print(bar('that', 'baz'))

当然,类实现不是必须的,下面是等效的纯函数式实现:

def func_bar(type_: str, x: str) -> str:
    dispatcher = {
        'this': this_bar,
        'that': that_bar
    }
    func = dispatcher[type_]
    return func(x)


def this_bar(x: str) -> str:
    return f'Doing this with {x}'


def that_bar(x: str) -> str:
    return f'Doing that with {x}'


print(func_bar('this', 'baz'))
print(func_bar('that', 'baz'))

有人觉得这里用类不合适,认为类应该只在需要持久化对象、跟踪可变状态时使用,但这个场景并不满足。实际上我的Foo实现里完全可以删掉__init__,直接在bar方法里把x: str作为参数,不用让bar引用实例属性self._x。

不过我认为基于类的实现有几个明显优势:

契约强制执行

如果后续要添加OtherFoo,只要继承IFoo接口,mypy这类静态类型检查工具就会确保我实现符合规范的bar方法。相比之下,函数式实现里如果不小心把函数命名成other_abr,还把这个拼写错误的函数加入调度器,静态分析工具没法发现这个问题。

当然,只要把other_bar加入func_bar的调度器,类型检查工具能检测到签名错误,但如果没把这个函数加进去,静态分析就发现不了签名不匹配的问题。

辅助方法/函数的组织

假设bar需要特定于实现的辅助方法,类实现里可以把辅助方法作为私有方法放在对应类中:

from abc import ABC, abstractmethod


class IFoo(ABC):
    @abstractmethod
    def bar(self) -> str:
        ...


class ThisFoo(IFoo):
    def __init__(self, x: str):
        self._x = x

    def bar(self) -> str:
        great_text = self._helper()
        return f'Doing this with {self._x}, {great_text}'

    def _helper(self) -> str:
        return f'{self._x} is great'


class ThatFoo(IFoo):
    def __init__(self, x: str):
        self._x = x

    def bar(self) -> str:
        great_text = self._helper()
        return f'Doing that with {self._x}, {great_text}'

    def _helper(self) -> str:
        return f'{self._x} is the greatest'

辅助方法各自归属对应的具体类,能清晰区分哪个辅助方法对应哪种实现。而函数式实现里最自然的方式是把辅助函数定义成独立的私有函数:

def this_bar(x: str) -> str:
    great_text = _this_bar_helper(x)
    return f'Doing this with {x}'


def _this_bar_helper(x: str) -> str:
    return f'{x} is great'


def that_bar(x: str) -> str:
    great_text = _that_bar_helper(x)
    return f'Doing that with {x}'


def _that_bar_helper(x: str) -> str:
    return f'{x} is the greatest'

但这样很难明确哪个辅助函数属于哪个公共函数(命名规范有帮助,但不如类方法可靠),尤其是存在多个辅助方法,其中一部分是特定实现的,一部分可能被多个实现共享时。

当然也可以把特定实现的辅助函数定义成对应公共函数的嵌套函数,但这会在公共函数里混合不同抽象层级,而且辅助函数的定义会打断代码,降低可读性。

避免嵌套参数传递的便利性

上面的代码也能体现这一点:类实现里,公共方法和私有方法都能访问self._x;而函数式实现里,辅助函数需要显式传递参数,这意味着函数签名会有更多样板代码,函数体也更长,因为要把参数传给辅助函数。当参数带默认值时,情况会更尴尬——需要在多个嵌套辅助函数的签名里重复输入相同的默认值。

有人可能觉得这是好事,因为能强制辅助函数明确所需参数,但如果要修改公共函数的签名,就得更新函数体(把新参数传给辅助函数)以及所有嵌套辅助函数的签名和实现,这非常繁琐。而类实现里,只要修改__init__方法的签名就可以了。


内容的提问来源于stack exchange,提问作者Yakkanomica

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 11:27:05