【问题标题】:Correct pattern for "instantiating the instance"“实例化实例”的正确模式
【发布时间】:2018-02-05 07:44:59
【问题描述】:

注意:在这个问题中,我使用 Python 语法和 Python 术语,但我要求使用独立于实际编程语言的 概念 方法,以确保答案有用面向更广泛的受众。

0级

让我们从几个函数开始:

def dog_tell(name, target, what):
    print(f"{name} the dog tells {what} to {target}")

def dog_bark(name, target):
    print(f"{name} the dog says 'Bark at you, {target}'")

def dog_wag(name, target):
    print(f"{name} the dog wags tail at {target}")

def dog_woof(name):
    print(f"{name} the dog woofs")

要使用这些函数,第一个参数应该是狗的名字,所以它会做一些有用的事情:

>>> dog_bark("Jake", "Finn")
Jake the dog says 'Bark at you, Finn'

1 级

现在,如果我们经常对同一只狗使用这些函数,那么将这些函数组合在一起是有意义的,并且只提供一次狗名。至少有两种方法可以做到这一点。

哎呀

OOP 方法是声明 Dog 类并为其提供名称:

class Dog(object):
    def __init__(self, name):
        self.name = name

    def tell(self, target, what):
        print(f"{self.name} the dog tells {what} to {target}")

    def bark(self, target):
        print(f"{self.name} the dog says 'Bark at you, {target}'")

    def wag(self, target):
        print(f"{self.name} the dog wags tail at {target}")

    def woof(self):
        print(f"{self.name} the dog woofs")

>>> jake = Dog("Jake")
>>> jake.bark("Finn")
Jake the dog says 'Bark at you, Finn'

功能性

另一种方法是currying:

name = "Jake"
jake_tell = lambda *args: dog_tell(name, *args)
jake_bark = lambda *args: dog_bark(name, *args)
jake_wag = lambda *args: dog_wag(name, *args)
jake_woof = lambda *args: dog_woof(name, *args)

>>> jake_bark("Finn")
Jake the dog says 'Bark at you, Finn'

这两种方法都提供相同的功能——它们本质上为所有的函数调用附加了一些重复的信息。

2 级

现在,我们进入我的问题的重点。如果杰克经常和芬恩交谈,我想让他们的谈话成为某种实体怎么办?我可以通过函数式方法轻松做到这一点:

target = "Finn"
jake_tell_finn = lambda *args: dog_tell(name, target, *args)
jake_bark_at_finn = lambda *args: dog_bark(name, target, *args)
jake_wag_at_finn = lambda *args: dog_wag(name, target, *args)

>> jake_bark_at_finn()
Jake the dog says 'Bark at you, Finn'

与实例化相比,这增加了一步,并将更多信息附加到函数调用中。

但是对于这种情况,正确的 OOP 方法是什么?如果我继续将部分状态信息添加到类方法中,有效地“实​​例化实例”、“实例化实例化的实例”等等,会怎样?

为了澄清意图,我想到了这样的事情:

>>> jake = Dog("Jake")
>>> jake_and_finn = jake.with_buddy("Finn")
>>> jake_and_finn.tell("dirty secrets")
Jake the dog tells dirty secrets to Finn

虽然我知道如何“按原样”实现此功能,但我的具体实现缺乏可扩展性、抽象性和美观性。它伤害了我的美感,我觉得我对这背后的非常基本的理论缺乏重要的理解。

【问题讨论】:

  • 您必须为每个目标重新定义柯里化函数的事实不是很有可扩展性。级别 1,您将 OOP 标记为“正确的 OOP 方法”。不过,并非所有语言都支持像 Python 这样的适当类。您还可以考虑更具可读性的解决方案:Dog("Jake").tell("dirty secrets").to("Finn")
  • @cricket_007,在 Jake the Dog 的上下文中使用您的替代方案肯定更具可读性,但实际上我显然不是在编写 Adventure Time 框架,而且我有不同的东西,比如 webdrivers,选择器参数替换、元素绑定等,都是按顺序发生的,使用先前的“阶段”作为基础,为每个方法添加额外的状态。但这很无聊,谁会阅读有关 webdrivers 的内容?
  • 好的,所以这个例子看起来像webdriver.withBrowser("chrome").setJavascriptEnabled(true).selectElement("a")...同样的想法。 Fluent API 就是所谓的
  • @cricket_007,事情太复杂了。虽然我很欣赏您对 fluent API 的建议,但就我而言,这不是我所要求的。举个具体的例子,我的部分代码在“类定义”时间执行,当 Python 构建所有视图、组件、子组件等的不可变树层次结构时。然后这个层次结构可以绑定到 webdriver,这将启用访问意见。然后,视图可以在给定参数替换的情况下被实例化,它们通过它们的选择器并将组件绑定到底层元素。它继续下去。
  • @cricket_007,最后的想法是,用这种“流利的 API”方式编写东西是不可能的,因为这会给框架用户带来太多不必要的内部知识,而我更喜欢不暴露实现细节,并保持外部接口非常简洁和干净。

标签: python class oop conceptual


【解决方案1】:

如果我理解正确的话,我认为你在谈论一个关于 graph 的抽象问题。

jakefinn是图中的两个节点,它们的通信通道是这个图中的边。因此,如果我们将每条“狗”及其通信通道构建为一个大图,我们可以使用两个类来描述这两个实例。

一个是Dog 作为节点,另一个是Tell 作为边。我们将每条狗实例化为DogDog 有一个函数接收一个参数来指定他的好友,并返回一个Tell 的实例作为有向边(根据您的需要确定,它可以是无向的。)

最后,我们使用Tell的实例进行通信。

例如:

jake = Dog("jake")
finn = Dog("finn")
jake_and_finn_tell = jake.with_buddy(finn)
# jake_and_finn_tell is an instance of class Tell.
jake_and_finn_tell.tell("dirty secrets")

【讨论】:

  • 很遗憾,您的回答并不能解决上述问题。 Finn 实际上可以是任何东西,这并不意味着我必须建立“沟通渠道”,Finn 只是作为可能使用的示例提供的。相反,我想找到将类实例与附加状态绑定的正确方法,形成“2级实例”或“实例实例”。
  • @toriningen 我只是以你的例子来解释。我要指出的是,抽象关系也可以实例化为实例。在您的示例中是“通信渠道”,但无论Finn 是什么,它都可以是任何东西。一个结构良好的关系只关心action 而不是其他任何东西,所以 Finn,甚至 Jake,绝对可以是任何东西。您不认为这个关系实例与您的函数方法非常相似吗?假设relation 包含sourcetarget,它们在您的函数方法中等于nametarget
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-25
  • 1970-01-01
  • 2019-11-23
相关资源
最近更新 更多