Чи можете ви реалізувати “об’єктно-орієнтоване” програмування без ключового слова класу?


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


2
Радий, що ти згоден. =) Хоча я не знаю Python досить добре, щоб дати ґрунтовну відповідь, вам може бути цікаво знати, що в Javascript типовий спосіб виконання OOP схожий на описаний вами "об'єкт функції" (хоча ми також маємо прототипічне успадкування, яке дозволяє об'єктам "ділитися" методами замість того, щоб мати окремі копії кожного методу на кожному об'єкті; я припускаю, що Python classробить аналогічну оптимізацію).
Іксрек

Якщо ви хочете детальної відповіді, вам слід задати інше запитання або приєднатись до кімнати чату, але коротка відповідь - якщо ви повністю ігноруєте успадкування прототипів, масиви тощо), це в основному вірно; більшість об'єктів JS - це не що інше, як словники строкових ключів до довільних значень. foo.bar()зазвичай ідентичний foo['bar'](), і в рідкісних випадках останній синтаксис є фактично корисним.
Іксрек


8
Це дійсно важливе питання на вашому шляху до фундаментального розуміння ООП. Якщо вам цікаво, ви можете прочитати мій запис у блозі, де я створюю просту об’єктну систему в JavaScript, не покладаючись на жодну з частин мови OOP. Ваш перший приклад має важливий недолік: Де ви писали object['method'](args), об'єкти Python насправді роблять еквівалент object['method'](object, args). Це стає актуальним, коли базовий клас викликає методи у дочірньому класі, наприклад, у Стратегічній схемі.
амон

13
Як зазначали інші, це сприйнятливе питання щодо ООП. Я скористаюся цією можливістю, проте зазначу, що це зовсім не те, як реальні банки представляють банківські рахунки. У банків немає об'єкта "рахунку", що змінюється, який змінюється при його дебетуванні та кредитуванні; вони мають лише список записів транзакцій, а потім обчислюють залишок зі списку транзакцій. Як хороша вправа, спробуйте застосувати цей механізм на різних мовах.
Ерік Ліпперт

Відповіді:


66

Вітаємо! Ви знову виявили відомий факт, що орієнтацію на об'єкти можна виконати без конкретної підтримки мови програмування. Це в основному так само, як об’єкти вводяться в схему в цій класичній підручнику . Зауважте, що в схемі немає classключового слова або якогось подібного еквівалента, і об'єкти можна створювати без рівних класів.

Однак, орієнтована на об'єкт парадигма була настільки успішною, що багато мов - і Python не є винятком - забезпечують вбудовану підтримку. Це просто, щоб розробникам було легше використовувати парадигму та надати стандартну форму орієнтації об’єктів для цієї мови. Це, по суті, та сама причина, чому багато мов забезпечують forцикл, хоча це може бути імітується за допомогою whileциклу лише з однією або двома додатковими рядками коду - простота у використанні .


"надати стандартну форму орієнтації на об'єкт для цієї мови" Чи чую я критику JavaScript там? ;)
jpmc26

1
@ jpmc26: навмисно. Здається, є деякі широко прийняті стандарти, як об’єкти створюються в JavaScript.
Док Браун

@overexchange: у вас є запитання?
Док Браун

1
@overexchange: Ну, те, що означає OOP, є дискусійним, існують різні школи думки, але визначення SICP в значній мірі те, що складається з 3-х пунктів у вашому питанні. Це, безумовно, про побудову абстракцій, але не забувайте пункти 2 і 3. Так, концепція OOP включає "зміну стану", але вона також дозволяє поняття "незмінних об'єктів" (наприклад, клас рядків у Java або C #, Python також є деякі змінні та деякі незмінні типи даних). І ваш перший приклад у вашому запитанні підтверджує це визначення, а також ваш другий приклад.
Док Браун

2
@overexchange: це сходить до визначення Ален Кей орієнтації на об'єкт (винахідник малої мови розмов). Ви знайдете вичерпну відповідь у цій stackoverflow.com/questions/2347973/… попередній статті про так. IMHO "повідомлення, що проходить між об'єктами" в сенсі SICP, просто означає не отримувати доступ до внутрішніх даних об'єкта безпосередньо, лише через "визначений протокол зв'язку". У мовах ОО, таких як Python, це може просто означати "виклик методу об'єкта".
Док Браун

13

Я погодився б, що перше визначення задовольняє трьом пунктам, які висунув ваш вчитель. Я не думаю, що нам потрібне ключове слово для нічого. Під обкладинками, що ще є об’єктом, крім структурою даних із різними типами даних та функціями для роботи з даними? Звичайно, функції є і даними ..

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

Що можна зробити з ключового слова класу тут, це те, що мова забезпечує підтримку такого типу конструкції поза коробкою, і вам не потрібно через усі обручі самостійно знову реалізовувати функціональність (що досить цікаве завдання в сама!). Не кажучи вже про весь синтаксичний цукор, який ви також можете отримати.


як щодо спадкування? Чи є ми вирішальними щодо підтипів / супертипів у режимі реального часу? Мій перший підхід може не розважати цим !!
переобмін

5
Спадкування жодним чином не потрібно для ООП. Ви можете реалізувати спадщину і в першому прикладі. Він не дуже "чистий", але все-таки можливий.
Захід

3
@Zavior, що коментар змушує мене думати про VB6. Об'єктно орієнтований без успадкування дійсно забезпечує менш чистий код, м'яко кажучи.
RubberDuck

1
@overexchange Коли ви думаєте про це, успадкування - це все про спільний код / ​​поведінку між класами. Ніщо не заважає вам постійно повторювати весь цей код. Було б дуже жахливо підтримувати, хоча. Є причина, чому спадщина існує :)
Захід

1
@Zavior У своїй найпростішій формі "підкласифікація" - це абстракція, яка говорить "перед тим, як повернути функцію диспетчеризації та передачі даних вищого порядку, яку я визначаю тут (на яку ми будемо робити вигляд, що це" клас "ха-ха ha), створити функцію "відправлення та передачі даних" надкласового класу, на яку посилається ThisParentFoo ". Це справді все, що є. Коли йдеться про наївне багатонаступне наслідування, це насправді все-таки є, але з застереженням ви вводите "алмазну проблему", через що багато успадкування відсмоктується.
zxq9

9

Звичайно, ви можете!

Мова самопрограмування - це динамічна, орієнтована на прототип, об'єктно-орієнтована мова, в якій все є об'єктом і немає ніякого сенсу класів чи чого б то не було. Він зосереджений на ідеї прототипних об'єктів та ідеї їх клонування замість того, щоб мати класи як шаблони того, як створювати об’єкти.

Слід перевірити http://www.selflanguage.org/ для отримання додаткової інформації. Я думаю, що це дуже цікаво, і якщо вам подобається OOP, це гарна ідея перевірити щось, що є не таким загальним.


0

Не завжди: це залежить від мови. Ви продемонстрували здатність робити це в Python, але (якщо ваше запитання має на увазі мовний агностик, незважаючи на тег Python), не всі мови можуть це зробити. Наприклад, Java, в основному, не може. Ігноруючи клас, який містить main, немає можливості визначити довільні методи / поля на об'єкті, визначеному в main, без ключового слова класу. Хоча анонімні класи існують, їм потрібен інтерфейс, і вони не можуть мати публічних членів, крім тих, що визначені в інтерфейсі. Хоча можна визначити власні інтерфейси, а потім зробити анонімні класи для них, це фактично те саме (але менш зручно), ніж просто використання класу.

Док Браун має чудову відповідь, але те, що я намагаюся зробити, - це те, що я впевнений, що існує принаймні одна мова, яка взагалі не дозволить вирішити ваше рішення.


Як початківець, щоб засвоїти поняття "Об'єктно-орієнтоване програмування", так, я намагаюся бути мовним агностиком. Я думаю, що "Док Браун" дав відповідь у тих же рядках, він сказав мені читати sicp text-chap3, який не має нічого спільного з жодним синтаксисом мови.
переобмін

Хочеться, щоб я назвав мову, яка абсолютно вимагає використання класів, щоб підтвердити свою відповідь. Але я знаю лише кілька мов, і на жаль, Java дозволяє обійтись. C ++ має структури, а вимкнення Javascript дозволяє те, що ви продемонстрували. Я підозрюю, що Smalltalk та Eiffel можуть вимагати заняття, оскільки я чую, що вони строго структуровані.
SkySpiral7

Як Док Браун, якби я вивчив oop за допомогою схеми, я б не ставив цього питання. На жаль, версія курсу SICP, яку я навчаю, використовує python.
переобмін

1
Кожна дійсна програма Java повинна містити classключове слово, щоб це не було чимось сюрпризом. Але ви абсолютно могли б реалізувати власну об’єктну систему поверх об’єктної системи Java, хоча я не знаю, чому ви хочете зробити таке.
Брайан Гордон

1. Java справді особлива в цьому плані, оскільки просто викинула всі інші ключові слова, які можуть використовуватися для створення користувацьких структур даних. Майже всі інші мови, які я знаю, мають або записи, або закриття. 2. Навіть у java ви можете запрограмувати на пам'ять, побудовану з масиву. І ви можете реалізувати орієнтацію на об'єкт всередині цього, використовуючи classключове слово лише тому, що мова вимагає від вас розміщувати свої функції в класах. Звичайно, це вкрай теоретично, але навіть у Java ви можете робити Орієнтацію об’єктів без вбудованих класів!
cmaster

0

Визначення вашого викладача повністю пропускає найважливіший момент об’єктно-орієнтованого програмування - те, що робить його корисним та унікальним. "Повідомлення передачі" - це купа дурниць, про які мріяли люди Smalltalk, і це було невдачею скрізь, де її намагалися. Справжня сила OOP - це щось відоме як заміна Ліскова , і хоча концепція є досить простою для опису та розуміння, основна реалізація є досить складною, що неможливо зробити правильно без підтримки рівня мови.

Ідея заміни Ліскова полягає в тому, що в будь-якому місці, де ваш код очікує змінну певного типу, він повинен мати можливість приймати будь-який тип, похідний від цього типу, і все ще працювати правильно, не маючи знання про деталі похідного типу.

Наприклад, GUI-кадри використовують повсюдно підміну Ліскова. Вони, як правило, мають базовий Controlклас, який може представляти "будь-який елемент управління", який визначає інтерфейс, який знає про основні дії, такі як малювання, зміна розміру та реагування на введення користувача. Якщо ви натиснете на елемент керування, фреймворк інтерфейсу викличе Clickметод на елементі управління, не піклуючись про тип управління, а потім дозволить керувати керуванням клацанням відповідним чином для власного класу. ButtonКонтроль повинен зробити що - то зовсім інше , коли натиснув на ніж TextBoxконтроль, щоб дати тільки один приклад.

Так, так, ви можете створити щось подібне до об'єктів, використовуючи описаний вище трюк вкладених функцій, але оскільки ви не можете отримати успадкування та заміну Ліскова таким чином, це надзвичайно обмежена заміна справжнього OOP.


Чи не можу я сказати мовою С "структура батьків {}", а потім "структуру дочірньої структури {структурна батьківська * ptr;}"? Це не є успадкуванням у синтаксисі мови, що не є oop?
переобмін

@overexchange: Це необов'язкова спроба підробити це, але компілятор не дозволить вам замінити одне іншим. (Ви не можете передати child*функцію, яка приймає parent*як аргумент, принаймні, не без набору тексту.) І ще гірше, структури C не можуть мати до них прив’язані методи, і немає підтримки для віртуальних методів, які є що змушує магію заміни Ліскова працювати, тому вам доведеться конструювати ВМТ вручну, що є складним процесом, який легко викрутити.
Мейсон Уілер

1
Ядро Linux використовує емуляцію декількох OO-методів, які всі повинні бути закодовані вручну без підтримки мови. Це призводить до безлічі можливостей для помилок, які, будучи Linux, врівноважуються ліберальним застосуванням Закону Лінуса. Так, це можливо - це доводить еквівалентність Тюрінга, але мій погляд, що виправитись без мовної підтримки все ще існує. Крім того, чому всі ці питання щодо C, коли питання стосувалося Python? У C в першу чергу неможливо виконати фокус вкладених функцій.
Мейсон Уілер

1
@overexchange З тих пір, як Java - "рай програмістів"?
Брандін

1
Передача повідомлень не була помилкою в системах Smalltalk, Erlang або навіть Java-OOP-стилі, де "повідомлення" означає щось інше, ніж "виклик функції" (сигнали Qt та слоти з черговою безпекою черги VS, старий маркетинг Java, використовуючи термін "повідомлення" коли це означає "виклик методу"). Повідомлення! = Виклики функцій. Справді обмін повідомленнями не лише успішний, він, здається, є єдиним способом, який ми знаємо, щоб писати масово сумісні та надійні системи. Це є ортогональним для реалізації OOP у стилі Java без ключового слова "class". Це можна зробити. Це не завжди корисно. Повідомлення знаходиться поруч.
zxq9

-1

Швидкий короткий відповідь

Так, програмісти можуть застосовувати об'єктно-орієнтоване програмування без "класів".

Довгий нудний широкий описовий відповідь

Існує кілька варіантів "Орієнтації на об'єкт", але все-таки перша концепція, яка спадає на думку багатьом програмістам, - це "Класи".

Так, програмісти можуть застосовувати об'єктно-орієнтоване програмування без "Класів", але, обмежується функціями та обмеженнями кожної мови програмування.

Ваше повідомлення позначено як Python , тому назва вашого запитання може бути більше схожа на "Як реалізувати об'єктно-орієнтоване програмування без класів у Python".

В даний час я використовую фразу "Об'єктно-орієнтоване програмування", щоб ідентифікувати інші варіанти, наприклад, "Прототипування" Javascript, або Visual Basic "На основі" або емуляція в "Чистому С", використовуючи "функтори".

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.