您可以在没有class关键字的情况下实现“面向对象”编程吗?


29

假设我们要提供银行“帐户”的抽象。这是function在Python中使用对象的一种方法:

def account():
    """Return a dispatch dictionary representing a bank account.

    >>> a = account()
    >>> a['deposit'](100)
    100
    >>> a['withdraw'](90)
    10
    >>> a['withdraw'](90)
    'Insufficient funds'
    >>> a['balance']
    10
    """
    def withdraw(amount):
        if amount > dispatch['balance']:
            return 'Insufficient funds'
        dispatch['balance'] -= amount
        return dispatch['balance']
    def deposit(amount):
        dispatch['balance'] += amount
        return dispatch['balance']
    dispatch = {'balance': 0,
                'withdraw': withdraw,
                'deposit': deposit}
    return dispatch

这是使用类型抽象(即classPython中的关键字)的另一种方法:

class Account(object):
    """A bank account has a balance and an account holder.

    >>> a = Account('John')
    >>> a.deposit(100)
    100
    >>> a.withdraw(90)
    10
    >>> a.withdraw(90)
    'Insufficient funds'
    >>> a.balance
    10
    """



    def __init__(self, account_holder):
        self.balance = 0
        self.holder = account_holder

    def deposit(self, amount):
        """Add amount to balance."""
        self.balance = self.balance + amount
        return self.balance

    def withdraw(self, amount):
        """Subtract amount from balance if funds are available."""
        if amount > self.balance:
            return 'Insufficient funds'
        self.balance = self.balance - amount
        return self.balance

我的老师通过引入class关键字并向我们展示了这些要点,开始了主题“面向对象的编程” :

面向对象编程

一种组织模块化程序的方法:

  • 抽象壁垒
  • 讯息传递
  • 将信息和相关行为捆绑在一起

您认为第一种方法足以满足上述定义吗?如果是,为什么我们需要class关键字来进行面向对象的编程?


2
很高兴您同意。=)尽管我不太了解Python,无法给出全面的答案,但您可能想知道,在Javascript中,典型的OOP方式类似于您描述的“函数对象”(尽管我们也有原型继承,允许对象“共享”方法,而不是在每个对象上都有每个方法的单独副本;我假设Python class做了类似的优化)。
Ixrec 2015年

如果您想获得详细的答案,则应该询问其他问题或加入聊天室,但是简短的答案是(如果您完全忽略了原型继承,数组等),那基本上是对的;大多数JS对象不过是任意值的字符串键字典。foo.bar()通常与相同foo['bar'](),在极少数情况下,后一种语法实际上很有用。
Ixrec 2015年


8
这是您对OOP进行基本了解的过程中一个非常重要的问题。如果您有兴趣,可以阅读我的博客文章,其中我使用JavaScript创建了一个简单的对象系统,而无需依赖该语言的任何OOP部分。您的第一个示例有一个重要的缺点:在编写代码的地方object['method'](args),Python对象实际上等效于object['method'](object, args)。当基类调用子类中的方法时,例如在“策略模式”中,这变得很重要。
阿蒙2015年

13
正如其他人指出的那样,这是有关OOP的可感知的问题。但是,我将借此机会指出,这根本不是真实银行代表银行帐户的方式。银行没有可变的“帐户”对象,该对象在您借记和贷记时会发生变化。他们具有事务的只读列表,然后从事务列表中计算余额。作为一项好练习,请尝试以多种语言实现该机制。
埃里克·利珀特

Answers:


66

恭喜你!您重新发现了众所周知的事实,即无需特定的编程语言支持就可以实现面向对象。与这本经典教科书中Scheme中引入对象的方式基本上相同。请注意,Scheme没有class关键字或某种等效项,并且即使没有类也可以创建对象。

但是,面向对象的范例非常成功,以至于许多语言(Python也不例外)为其提供了内置支持。这仅仅是为了使开发人员更容易使用该范例,并为该语言提供标准的面向对象形式。for尽管可以使用while仅带一行或两行附加代码的循环来模拟它,但许多语言都提供了循环,这基本上是相同的原因-简单易用


“为该语言提供面向对象的标准形式”我听到那里有对JavaScript的批评吗?;)
jpmc26 2015年

1
@ jpmc26:不是故意的。似乎已经有一些广泛接受的标准使用JavaScript创建对象。
布朗

@overexchange:您有问题要问吗?
布朗

1
@overexchange:嗯,OOP的含义值得商,,有不同的思想流派,但是SICP的定义几乎是您问题中3个要点中的一个。它肯定是关于构建抽象的,但不要忘记第2点和第3点。是的,OOP概念包含“状态更改”,但它也允许“不可变对象”的概念(例如Java或C#中的字符串类,Python)具有一些可变的和不可变的数据类型)。问题中的第一个例子以及第二个例子都证实了这一定义。
布朗

2
@overexchange:这可以回溯到Alain Kay对面向对象的定义(闲聊语言的发明者)。您可以在此stackoverflow.com/questions/2347973/…前SO文章中找到完整的答案。在SICP意义上,恕我直言“在对象之间传递消息”仅意味着不通过“定义的通信协议”直接访问对象的内部数据。在像Python这样的OO语言中,这可能仅意味着“调用对象的方法”。
布朗

13

我同意第一个定义满足您老师提出的三点要求。我认为我们不需要 class关键字。在幕后,除了具有不同类型的数据和用于处理数据的功能的数据结构之外,还有什么对象?当然,这些功能也是数据。

我会更进一步地说,进行面向对象的编程并不太取决于您的语言提供的关键字,如果您愿意,可以使用C语言进行面向对象的编程!实际上,Linux内核采用了这种技术。

您可以从class关键字中推断出,该语言提供了对这种结构的即开即用支持,并且您不需要自己花时间去重新实现功能(这是一个非常有趣的任务)本身!)。更不用说您可能会获得的所有语法糖。


那继承呢?我们对实时亚型/超型至关重要吗?我的第一个方法可能不会招待这个!
过度兑换

5
OOP绝对不需要继承。您也可以在第一个示例中实现继承。它不是很“干净”,但可能都是一样的。
Zavior

3
@Zavior的评论让我想到了VB6。毫不客气地说,没有继承的面向对象确实确实减少了干净的代码。
RubberDuck

1
@overexchange当您考虑它时,继承就是在类之间共享通用代码/行为。没有什么可以阻止您一直重复所有这些代码。维护起来太糟糕了。存在继承存在一个原因:)
Zavior 2015年

1
@Zavior在其最基本的形式中,“子类化”是一个抽象,它表示“在返回我在此定义的高阶调度和数据具有函数之前(我们假装是一个”类”) ha),实例化ThisParentFoo引用的“超类”调度和具有数据的功能”。真的就是全部。当涉及到朴素的多重继承时,它实际上仍然是全部,但是请注意,您引入了“钻石问题”,这就是多重​​继承很烂的原因。
zxq9

9

当然可以!

自我编程语言是一种动态的基于原型的面向对象语言,其中的所有内容都是对象,并且没有类或任何意义。它着重于原型对象的思想和克隆它们的思想,而不是将类作为如何创建对象的模板。

您应该查看http://www.selflanguage.org/了解更多信息。我认为这很有趣,如果您喜欢OOP,最好检查一下不常见的东西。


0

并非总是如此:这取决于语言。您已经展示了使用Python做到这一点的能力,但是(如果您的问题是使用Python标记而不考虑语言的话),并不是所有的语言都可以做到这一点。以Java为例,大多数情况是不能。忽略包含main的类,如果没有class关键字,就无法在main中定义的对象上定义任意方法/字段。尽管确实存在匿名类,但它们需要一个接口,并且除了接口中定义的公共成员外,它们不能具有任何公共成员。尽管可以定义自定义接口然后为它们创建匿名类,但这实际上与简单地使用类相同(但不太方便)。

布朗博士(Doc Brown)有一个很好的答案,但我要说的是,我确定至少有一种语言根本不允许您使用该解决方案。


作为一个初学者,要学习“面向对象编程”的概念,可以尝试与语言无关。我认为“ Doc Brown”已经给出了相同的答案,他告诉我阅读sicp text-chap3,它与任何语言语法都没有关系。
过度兑换

我希望我能命名一种绝对需要使用类来验证我的答案的语言。但是我只知道几种语言,可惜Java可以解决。C ++具有结构,Javascript完全支持您演示的内容。我怀疑Smalltalk和Eiffel可能需要上课,因为我听说它们是严格结构化的。
SkySpiral7

作为Doc布朗博士,如果我学会了使用方案的oop,我就不会问这个问题。不幸的是,我正在学习的SICP课程版本使用python。
过度兑换

1
每个有效的Java程序都必须包含class关键字,这不足为奇。但是您绝对可以在Java对象系统之上实现自己的对象系统,尽管我不知道您为什么要这样做。
布赖恩·戈登

1. Java在这方面确实很特别,因为Java只是丢​​弃了所有其他可用于创建自定义数据结构的关键字。我所知道的几乎所有其他语言都有记录或闭包。2.即使在Java中,也可以在由数组构建的内存上编程。而且,您可以在其中使用class关键字实现对象定向,这仅是因为该语言要求您将函数放在类中。当然,这是非常理论的,但是即使在Java中,也可以在没有内置类的情况下进行对象定向!
cmaster

0

您老师的定义完全错过了面向对象编程的最重要要点,这一点使它变得有用且独特。“消息传递”是Smalltalk员工梦a以求的一堆废话,到处都是失败的尝试。OOP的真正威力就是所谓的Liskov替代,虽然该概念很容易描述和理解,但其基础实现却十分复杂,以至于没有语言级别的支持,根本不可能正确地做到这一点。

Liskov替换的想法是,在您的代码期望某个类型的变量的任何地方,它都应该能够接受从该类型派生的任何类型,并且仍然可以正常工作而无需了解派生类型的详细信息。

例如,GUI框架到处都使用Liskov替换。它们倾向于具有Control可以表示“任何控件” 的基类,该基类定义了一个接口,该接口了解基本操作,例如绘制,调整大小以及响应用户输入。如果单击控件,则UI框架将Click在控件上调用方法,而不必关心控件的种类,然后让控件以适合其自己类的方式处理单击。一个Button控制应该做的事情不是点击广告时完全不同的TextBox控制,举一个例子。

因此,可以,您可以使用上述嵌套函数技巧创建类似于对象的对象,但是由于无法以这种方式获得继承和Liskov替换,因此它是真正OOP的极有限的替代品。


在C语言中,我不能说“ struct parent {}”,然后再说“ struct child {struct parent * ptr;}”吗?这不是非oop语言语法的继承吗?
外汇兑换

@overexchange:这是非OO的伪造尝试,但是编译器不允许您一个替代另一个。(您不能将a传递child*给以a parent*作为参数的函数,至少在没有类型转换的情况下如此。)更糟糕的是,C结构体无法绑定方法,并且不支持虚拟方法。是什么使Liskov替代魔术发挥作用,所以您必须手工构造VMT,这是一个复杂的过程,很容易搞砸。
梅森惠勒

1
Linux内核使用了几种OO技术的仿真,所有这些技术都必须在没有语言支持的情况下手动编码。这为Bug带来了很多机会,而对于Linux,这些漏洞被Linus定律的自由应用所抵消。是的,有可能做到这一点-等效性证明了这一点-但我的观点是,没有语言支持,很难正确行事。另外,为什么所有这些关于C的问题都是关于Python的?在C语言中,不可能一开始就做嵌套函数。
梅森惠勒2015年

1
@overexchange从什么时候开始Java是“程序员天堂”?
布兰登2015年

1
在Smalltalk,Erlang甚至Java风格的OOP系统中,消息传递并非没有失败,在Java的OOP系统中,“消息”的含义不同于“函数调用”(Qt的带有线程安全队列的信号和插槽,相对于使用术语“消息”的旧Java市场营销当它表示“方法调用”时)。消息!=函数调用。真正的消息传递不仅成功,而且它似乎是我们知道编写大规模并发和健壮系统的唯一方法。这与不带“ class”关键字的Java风格的OOP正交。它可以完成。它并不总是有用的。消息传递不重要。
zxq9

-1

快速简短答案

是的,程序员可以在没有“类”的情况下应用面向对象的编程。

长时间无聊的描述性答案

“ Object Orientation”有多种变体,很多程序员想到的第一个概念就是“ Classes”。

是的,程序员可以在不使用“类”的情况下应用面向对象的编程,但是受限于每种编程语言的功能和限制。

您的帖子被标记为Python,因此,您的问题标题可能更像是“如何在Python中实现无需类的面向对象编程”。

我目前使用“面向对象和面向类的编程”短语来从其他变体中识别,例如Javascript的“ Prototyping”或Visual Basic的“ Based”,或使用“ functors”在“ Pure C”中进行仿真。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.