Можете ли вы реализовать «объектно-ориентированное» программирование без ключевого слова класса?

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

Вот еще один подход, использующий абстракцию типов (т. classЕ. Ключевое слово в Python):

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ключевое слово для объектно-ориентированного программирования?

overexchange
источник
2
Рад, что вы согласны. =) Хотя я не знаю Python достаточно хорошо, чтобы дать исчерпывающий ответ, вам может быть интересно знать, что в Javascript типичный способ выполнения ООП похож на описываемый вами «функциональный объект» (хотя у нас также есть наследование прототипа, которое позволяет объектам «делиться» методами вместо того, чтобы иметь отдельные копии каждого метода для каждого объекта; я предполагаю, что Python classвыполняет аналогичную оптимизацию).
Ixrec
Если вы хотите получить подробный ответ, вам следует задать другой вопрос или присоединиться к чату, но короткий ответ (если вы полностью игнорируете наследование прототипов, массивы и т. Д.), Это в основном верно; большинство объектов JS - это не что иное, как словари строковых ключей для произвольных значений. foo.bar()обычно идентичен foo['bar'](), и в редких случаях последний синтаксис действительно полезен.
Ixrec
8
Это действительно важный вопрос на пути к фундаментальному пониманию ООП. Если вам интересно, вы можете прочитать мой пост в блоге, где я создаю простую объектную систему на JavaScript, не полагаясь ни на одну из частей ООП языка. Ваш первый пример имеет важный недостаток: где бы вы ни писали object['method'](args), объекты Python фактически эквивалентны object['method'](object, args). Это становится актуальным, когда базовый класс вызывает методы в дочернем классе, например, в шаблоне стратегии.
Амон
13
Как уже отмечали другие, это проницательный вопрос об ООП. Однако я воспользуюсь этой возможностью, чтобы отметить, что реальные банки представляют банковские счета совсем не так. В банках нет изменяемого объекта «счета», который изменяется при его дебете и зачислении; у них есть список транзакций только для записи, а затем вычисляется баланс из списка транзакций. В качестве хорошего упражнения попробуйте реализовать этот механизм на разных языках.
Эрик Липперт

Ответы:

66

Поздравляем! Вы заново обнаружили общеизвестный факт, что объектная ориентация может быть осуществлена ​​без специальной поддержки языка программирования. Это в основном то же самое, как объекты вводятся в схему в этом классическом учебнике . Обратите внимание, что в Scheme нет classключевого слова или какого-либо эквивалента, и объекты можно создавать, не имея даже классов.

Однако объектно-ориентированная парадигма оказалась настолько успешной, что многие языки - и Python не исключение - предоставляют встроенную поддержку для нее. Это просто для того, чтобы разработчикам было проще использовать эту парадигму и предоставить стандартную форму объектной ориентации для этого языка. По сути, это та же самая причина, по которой многие языки предоставляют forцикл, хотя его можно эмулировать, используя whileцикл с одной или двумя дополнительными строками кода - просто простота использования .

Док Браун
источник
«Обеспечить стандартную форму объектной ориентации для этого языка». Слышу ли я там критику JavaScript? ;)
jpmc26
1
@ jpmc26: не намеренно. И кажется, что есть некоторые общепринятые стандарты, как объекты создаются в JavaScript.
Док Браун
@overexchange: у вас есть вопрос?
Док Браун
1
@overexchange: Что значит ООП, является дискуссионным, существуют разные школы, но определение SICP в значительной степени совпадает с 3 пунктами в вашем вопросе. Определенно речь идет о построении абстракций, но не забывайте пункты 2 и 3. Да, концепция ООП включает в себя «изменение состояния», но она также допускает концепцию «неизменяемых объектов» (например, строковый класс в Java или C #, Python имеет некоторые изменяемые и некоторые неизменяемые типы данных). И ваш первый пример в вашем вопросе подтверждает это определение, а также ваш второй пример.
Док Браун
2
@overexchange: это восходит к определению объектной ориентации Алена Кея (изобретатель языка небольших разговоров). Вы найдете исчерпывающий ответ в этой статье stackoverflow.com/questions/2347973/… бывшей SO. ИМХО «передача сообщений между объектами» в смысле SICP означает просто не обращаться к внутренним данным объекта напрямую, только через «определенный протокол связи». В ОО-языках, таких как Python, это может означать «вызов метода объекта».
Док Браун
13

Я бы согласился, что первое определение удовлетворяет трем пунктам, которые сделал ваш учитель. Я не думаю, что нам нужно ключевое слово класса для чего-либо. Под покровом, что еще является объектом, кроме структуры данных с различными типами данных и функциями для работы с данными? Конечно, функции также являются данными.

Я бы пошел еще дальше и сказал, что выполнение объектно-ориентированного программирования не так сильно зависит от ключевых слов, которые предоставляет ваш язык, вы можете заниматься объектно-ориентированным программированием на C, если вы этого хотите! На самом деле ядро Linux использует такие методы.

Из ключевого слова class вы можете сделать вывод, что язык предоставляет поддержку для такого рода конструкций из коробки, и вам не нужно проходить через все циклы, чтобы заново реализовать функциональность самостоятельно (что довольно забавная задача в сам!). Не говоря уже о синтаксическом сахаре, который вы можете получить.

Zavior
источник
как насчет наследства? Важны ли мы в отношении подтипов / супертипов в реальном времени? Мой первый подход не может развлечь это !!
сверхобмена
5
Наследование ни в коем случае не требуется для ООП. Вы также можете реализовать наследование в своем первом примере. Это не очень "чисто", но возможно все же.
Завиор
3
@Zavior этот комментарий заставляет меня думать о VB6. Объектно-ориентированные без наследования действительно делают, мягко говоря, менее чистый код.
RubberDuck
1
@overexchange Когда вы думаете об этом, наследование - это разделение общего кода / поведения между классами. Ничто не мешает вам повторять весь этот код все время. Было бы очень ужасно поддерживать, хотя. Есть причина, по которой существует наследование :)
Zavior
1
@Zavior В своей самой основной форме «подклассификация» - это абстракция, которая говорит: «прежде чем вы вернете функцию диспетчеризации и передачи данных, имеющую высший порядок, которую я здесь определяю (которую мы будем притворяться, это« класс », ха-ха»). ha), создать экземпляр функции «superclass» для отправки и передачи данных, на которую ссылается ThisParentFoo ». Это действительно все, что есть. Когда дело доходит до наивного множественного наследования , что на самом деле до сих пор все это есть, но с оговоркой , что вы ввести «проблему бриллианта», поэтому множественное наследование отстоя.
zxq9
9

Конечно вы можете!

Язык Self-программирования - это динамический объектно-ориентированный язык, основанный на прототипах, в котором все является объектом, и в нем нет смысла в классах или чем-то еще. Он сфокусирован на идее прототипных объектов и идее клонирования их вместо использования классов в качестве шаблонов создания объектов.

Вы должны проверить http://www.selflanguage.org/ для получения дополнительной информации. Я думаю, что это очень интересно, и если вам нравится ООП, то стоит проверить что-то необычное.

DraQ
источник
0

Не всегда: это зависит от языка. Вы продемонстрировали способность делать это в Python, но (если ваш вопрос не зависит от языка, несмотря на тег Python) не все языки могут это делать. Ява, например, в основном не может. Игнорируя класс, содержащий main, невозможно определить произвольные методы / поля для объекта, определенного в main, без ключевого слова class. Хотя анонимные классы существуют, им требуется интерфейс, и они не могут иметь открытых членов, кроме тех, которые определены в интерфейсе. Хотя можно определить пользовательские интерфейсы, а затем создать для них анонимные классы, это фактически то же самое (но менее удобно), чем простое использование класса.

У Дока Брауна есть отличный ответ, но я хочу сказать, что я уверен, что есть хотя бы один язык, который вообще не допускает вашего решения.

SkySpiral7
источник
Как новичок, чтобы выучить концепцию «объектно-ориентированного программирования», я стараюсь быть независимым от языка. Я думаю, что «Док Браун» дал ответ в тех же строках, он сказал мне прочитать sicp text-chap3, который не имеет никакого отношения к синтаксису языка.
сверхобмена
Я хотел бы назвать язык, который абсолютно требует использования классов для подтверждения моего ответа. Но я знаю только несколько языков, и, увы, Java позволяет обойти это. В C ++ есть структуры, а Javascript позволяет использовать то, что вы продемонстрировали. Я подозреваю, что Smalltalk и Eiffel могут потребовать занятия, поскольку я слышал, что они строго структурированы.
SkySpiral7
Как док Браун, если бы я научился использовать схему, я бы не задавал этот вопрос. К сожалению, версия курса SICP, которую я изучаю, использует python.
сверхобмена
1
Каждая действующая Java-программа должна содержать classключевое слово, так что это неудивительно. Но вы абсолютно могли бы реализовать свою собственную объектную систему поверх объектной системы Java, хотя я не знаю, почему вы хотите сделать такую ​​вещь.
Брайан Гордон
1. Java действительно особенная в этом отношении, поскольку она просто отбросила все другие ключевые слова, которые могут использоваться для создания пользовательских структур данных. Почти все другие языки, о которых я знаю, имеют записи или закрытия. 2. Даже в Java вы можете программировать в памяти, построенной из массива. И вы можете реализовать объектную ориентацию внутри этого, используя classключевое слово только потому, что язык требует, чтобы вы поместили свои функции в классы. Конечно, это чрезвычайно теоретически, но даже в Java вы можете сделать объектную ориентацию без встроенных классов!
cmaster
0

Определение вашего учителя полностью пропускает самый важный момент объектно-ориентированного программирования, то есть то, что делает его полезным и уникальным. «Передача сообщений» - это чепуха, придуманная ребятами из Smalltalk, и везде она проваливалась. Истинная сила ООП - это то, что известно как подстановка Лискова , и, хотя концепция довольно проста для описания и понимания, базовая реализация достаточно сложна, что по существу невозможно сделать правильно без поддержки на уровне языка.

Идея подстановки Лискова заключается в том, что везде, где ваш код ожидает переменную определенного типа, он должен иметь возможность принимать любой тип, производный от этого типа, и при этом работать правильно, не зная деталей о производном типе.

Например, GUI-фреймворки используют подстановку Лискова повсюду. Как правило, они имеют базовый Controlкласс, который может представлять «любой элемент управления», который определяет интерфейс, который знает об основных действиях, таких как рисование, изменение размера и реагирование на ввод пользователя. Если вы щелкнете по элементу управления, инфраструктура пользовательского интерфейса вызовет Clickметод для элемента управления, не заботясь о том, что это за элемент управления, а затем позволит элементу управления обработать щелчок соответствующим образом для своего собственного класса. Элемент Buttonуправления должен делать что-то совершенно иное при нажатии, чем элемент TextBoxуправления, чтобы дать только один пример.

Так что да, вы можете создать что-то похожее на объекты, используя описанный выше трюк с вложенными функциями, но поскольку вы не можете получить наследование и подстановку Лискова таким образом, это очень ограниченная замена истинного ООП.

Мейсон Уилер
источник
На языке C я не могу сказать «struct parent {}», а затем «struct child {struct parent * ptr;} '? Это не наследование в синтаксис языка без ООП?
сверхобмена
@overexchange: Это не попытка фальсификации, но не компилятор, но компилятор не позволит вам заменить один другим. (Вы не можете передать a child*в функцию, которая принимает parent*в качестве аргумента a , по крайней мере, без передачи типа.) И что еще хуже, структуры C не могут иметь привязанные к ним методы, и нет поддержки виртуальных методов, которые что заставляет работать магию подстановки Лискова, так что вам приходится создавать VMT вручную, что является сложным процессом, который легко испортить.
Мейсон Уилер
1
Ядро Linux использует эмуляцию нескольких методов ОО, которые все должны быть написаны вручную без языковой поддержки. Это приводит к множеству возможностей для ошибок, которые, будучи Linux, уравновешиваются либеральным применением закона Линуса. Да, это возможно - эквивалентность по Тьюрингу доказывает это - но моя точка зрения, что без языковой поддержки очень трудно получить правильные права, остается в силе. Кроме того, почему все эти вопросы о C, когда вопрос был о Python? В C не возможно сделать трюк с вложенными функциями в первую очередь.
Мейсон Уилер
1
@overexchange С каких это пор Java - это «рай для программистов»?
Брандин
1
Передача сообщений не была сбоем в Smalltalk, Erlang или даже в ООП-системах в стиле Java, где «сообщение» означает нечто иное, чем «вызов функции» (сигналы и слоты Qt с потокобезопасной очередью против старого маркетинга Java с использованием термина «сообщение» когда это означает "вызов метода"). Сообщения! = Вызовы функций. Подлинный обмен сообщениями не только успешен, но и является единственным известным нам способом написания одновременно работающих и надежных систем. Это ортогонально реализации ООП в стиле Java без ключевого слова class. Это можно сделать. Это не всегда полезно. Обмен сообщениями не относится к делу.
zxq9
-1

Быстрый короткий ответ

Да, программисты могут применять объектно-ориентированное программирование без «классов».

Длинный Скучный Обширный Описательный Ответ

Существует несколько разновидностей «объектной ориентации», хотя первое понятие, которое приходит на ум многим программистам, - это «классы».

Да, программисты могут применять объектно-ориентированное программирование без «классов», но оно ограничено возможностями и ограничениями каждого языка программирования.

Ваш пост помечен как Python , поэтому заголовок вашего вопроса может быть больше похож на «Как реализовать объектно-ориентированное программирование без классов в Python».

В настоящее время я использую фразу «объектно-ориентированное программирование», чтобы идентифицировать другие вариации, такие как «Прототипирование» в Javascript, Visual Basic «на основе» или эмуляцию в «Pure C» с использованием «функторов».

umlcat
источник