内部类方法应该返回值还是只修改实例变量?

Posted

技术标签:

【中文标题】内部类方法应该返回值还是只修改实例变量?【英文标题】:Should internal class methods return values or just modify instance variables? 【发布时间】:2012-06-16 13:22:44 【问题描述】:

我正在创建一个查询构建器类,该类将有助于从 URL 参数构造 mongodb 查询。除了使用基本的语言结构和使用 django 的内置模型之外,我从来没有做过太多的面向对象编程,也没有设计过供其他人使用的类。

所以我有这个QueryBuilder

class QueryHelper():
    """
    Help abstract out the problem of querying over vastly
    different dataschemas.
    """

    def __init__(self, collection_name, field_name, params_dict):
        self.query_dict = 
        self.params_dict = params_dict
        db = connection.get_db()
        self.collection = db[collection_name]

    def _build_query(self):
        # check params dict and build a mongo query
        pass

现在在_build_query 中,我将检查params_dict 并填充query_dict 以便将其传递给mongo 的find() 函数。 在这样做时,我只是想知道是否有绝对正确的方法来判断_build_query 是否应该返回一个字典,或者它是否应该只修改self.query_dict。由于它是一种内部方法,我认为只修改self.query_dict 是可以的。有没有正确的方法(pythonic)方法来解决这个问题?这只是愚蠢而不是重要的设计决定吗?任何帮助表示赞赏。

【问题讨论】:

我在这个问题上有 7 年的背景,如果其他人也有同样的疑问,我发现的最佳策略与以下想法非常相似:-blog.cleancoder.com/uncle-bob/2012/08/13/…-yegor256.com/elegant-objects.html-@ 987654323@ 【参考方案1】:

修改self.query_dict 完全没问题,因为面向对象编程的整个理念是方法可以修改对象的状态。只要方法完成后对象处于一致状态,就可以了。 _build_query 是一个内部方法这一事实并不重要。您可以选择在__init__之后调用_build_query,以便在创建对象时已经构造查询。

该决定主要用于测试目的。出于测试目的,可以方便地单独测试每个方法,而不必测试整个对象的状态。但这不适用于这种情况,因为我们讨论的是内部方法,因此您自己决定何时调用该方法,而不是其他对象或其他代码。

【讨论】:

【参考方案2】:

最好返回一个值,因为它允许您将所有属性修改保存在一个位置 (__init__)。此外,这使得以后扩展代码更容易;假设你想在子类中覆盖_build_query,那么覆盖方法可以只返回一个值,而不需要知道要设置哪个属性。这是一个例子:

class QueryHelper(object):
    def __init__(self, param, text):
        self._param = param
        self._query = self._build_query(text)

    def _build_query(self, text):
        return text + " and ham!"

class RefinedQueryHelper(QueryHelper):
    def _build_query(self, text):
        # no need to know how the query object is going to be used
        q = super(RefinedQueryHelper, self)._build_query()
        return q.replace("ham", "spam")

对比“二传手版本”:

class QueryHelper(object):
    def __init__(self, param, text):
        self._param = param
        self._build_query(text)

    def _build_query(self, text):
        self._query = text + " and ham!"

class RefinedQueryHelper(QueryHelper):
    def _build_query(self, text):
        # what if we want to store the query in __query instead?
        # then we need to modify two classes...
        super(RefinedQueryHelper, self)._build_query()
        self._query = self._query.replace("ham", "spam")

如果您确实选择设置属性,您可能需要调用方法_set_query 以清楚起见。

【讨论】:

您也可以将_build_query 设为property 属性,从而有效地实现两全其美。【参考方案3】:

如果您返回任何东西,我建议self。从实例方法返回self 便于方法链接,因为每个返回值都允许对同一对象调用另一个方法:

foo.add_thing(x).add_thing(y).set_goal(42).execute()

这有时被称为“流利的”API。

然而,虽然 Python 允许为不可变类型(例如 intstr)提供方法链接,但它不为可变容器的方法(例如 listset)提供方法链接(设计上如此),因此可以说是不是“Pythonic”为你自己的可变类型做这件事。尽管如此,许多 Python 库确实具有“流畅”的 API。

缺点是这样的 API 会使调试变得更加困难。由于您执行整个语句或不执行任何语句,因此您无法轻易地在语句的中间点看到对象。当然,我通常会发现 print 非常适合调试 Python 代码,所以我会在任何我感兴趣的返回值的方法中抛出一个 print

【讨论】:

.execute() 是不必要的。您可以使用.all().first().one() 触发执行。 当然,如果你有不止一种方法可以实际做某事,那就太好了。【参考方案4】:

虽然对象的方法直接修改其状态是很常见的,但有时将对象作为自己的“客户端”并通过(通常)私有访问方法间接访问自己可能是有利的。在 Python 中,您可以使用内置的 property() 类/函数轻松完成此操作。

这样做可以提供更好的encapsulation 以及随之而来的好处(主要是实现细节的隔离)。然而,这样做可能不切实际,因为它需要太多额外的代码,而且通常速度较慢,这可能会对性能产生不可接受的负面影响——因此必须经常根据这个理想进行权衡。

【讨论】:

以上是关于内部类方法应该返回值还是只修改实例变量?的主要内容,如果未能解决你的问题,请参考以下文章

java中什么是实例变量

七类的高级特性

内部类和外部类的实例变量可以共存

5.2访问限制

JAVA类与对象---实例变量与类变量的区别,实例方法和类方法的区别

static 关键字