【发布时间】: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