Puoi implementare la programmazione "orientata agli oggetti" senza la parola chiave class?


29

Supponiamo di voler fornire un'astrazione di un "conto" in una banca. Ecco un approccio, usando un functionoggetto in 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

Ecco un altro approccio che utilizza l'astrazione del tipo (ovvero la classparola chiave in 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

Il mio insegnante ha iniziato l'argomento "Programmazione orientata agli oggetti" introducendo la classparola chiave e mostrandoci questi punti elenco:

Programmazione orientata agli oggetti

Un metodo per organizzare programmi modulari:

  • Barriere di astrazione
  • Messaggio che passa
  • Raggruppamento di informazioni e comportamenti correlati

Pensi che il primo approccio sarebbe sufficiente per soddisfare la definizione di cui sopra? Se sì, perché abbiamo bisogno della classparola chiave per eseguire una programmazione orientata agli oggetti?


2
Sono contento che tu sia d'accordo. =) Anche se non conosco Python abbastanza bene da dare una risposta completa, potresti essere interessato a sapere che in Javascript il modo tipico di fare OOP è simile all '"oggetto funzione" che descrivi (anche se abbiamo anche un'eredità prototipale che consente agli oggetti di "condividere" metodi invece di avere copie separate di ciascun metodo su ogni oggetto; suppongo che Python classesegua un'ottimizzazione simile).
Ixrec,

Se si desidera una risposta dettagliata, è necessario porre un'altra domanda o unirsi alla chat room, ma la risposta breve è (se si ignora completamente l'ereditarietà del prototipo, gli array, ecc.) Questo è fondamentalmente vero; la maggior parte degli oggetti JS non sono altro che dizionari di chiavi di stringa a valori arbitrari. foo.bar()di solito è identico a foo['bar'](), e in rare occasioni quest'ultima sintassi è effettivamente utile.
Ixrec,


8
Questa è una domanda davvero importante sulla strada per una comprensione fondamentale di OOP. Se sei interessato, puoi leggere un mio post sul blog in cui creo un semplice sistema a oggetti in JavaScript senza fare affidamento su nessuna delle parti OOP della lingua. Il tuo primo esempio ha un difetto importante: dove scriverai object['method'](args), gli oggetti Python fanno effettivamente l'equivalente di object['method'](object, args). Ciò diventa rilevante quando una classe base chiama metodi in una classe figlio, ad esempio nel modello di strategia.
amon,

13
Come altri hanno notato, questa è una domanda percettiva su OOP. Ne approfitterò comunque per notare che non è affatto così che le banche reali rappresentano i conti bancari. Le banche non dispongono di un oggetto "conto" mutabile che cambia quando viene addebitato e accreditato; hanno un elenco di transazioni di sola scrittura e quindi calcolano il saldo dall'elenco delle transazioni. Come buon esercizio, prova ad attuare quel meccanismo in varie lingue.
Eric Lippert,

Risposte:


66

Congratulazioni! Hai riscoperto il fatto ben noto che l'orientamento agli oggetti può essere fatto senza il supporto di un linguaggio di programmazione specifico. È sostanzialmente lo stesso modo in cui gli oggetti vengono introdotti in Scheme in questo classico libro di testo . Si noti che Scheme non ha una classparola chiave o un tipo di equivalente e gli oggetti possono essere creati senza classi pari.

Tuttavia, il paradigma orientato agli oggetti ha avuto un tale successo che molti linguaggi - e Python non fa eccezione - forniscono supporto integrato per esso. Questo è semplicemente per facilitare agli sviluppatori l'uso del paradigma e fornire una forma standard di orientamento agli oggetti per quel linguaggio. È essenzialmente lo stesso motivo per cui molte lingue forniscono un forciclo, sebbene possa essere emulato usando un whileciclo con solo una o due righe di codice aggiuntive - semplicemente facilità d'uso .


"per fornire una forma standard di orientamento agli oggetti per quel linguaggio" Sento una critica a JavaScript? ;)
jpmc26

1
@ jpmc26: non intenzionalmente. E sembra che ci siano alcuni standard ampiamente accettati su come gli oggetti vengono creati in JavaScript.
Doc Brown,

@overexchange: hai una domanda da porre?
Doc Brown,

1
@overexchange: Beh, ciò che OOP significa è discutibile, ci sono diverse scuole di pensiero, ma la definizione SICP è praticamente quella dei 3 punti elenco nella tua domanda. Si tratta sicuramente di costruire astrazioni, ma non dimenticare i punti 2 e 3. Sì, il concetto OOP racchiude il "cambio di stato", ma consente anche il concetto di "oggetti immutabili" (come la classe di stringhe in Java o C #, Python ha alcuni tipi di dati mutabili e anche immutabili). E il tuo primo esempio nella tua domanda conferma questa definizione e il tuo secondo esempio.
Doc Brown,

2
@overexchange: questo risale alla definizione di orientamento agli oggetti di Alain Kay (l'inventore del linguaggio parlato). Troverai una risposta esaustiva in questo stackoverflow.com/questions/2347973/… precedente articolo SO. "Messaggio che passa tra oggetti" IMHO nel senso SICP significa semplicemente non accedere direttamente ai dati interni di un oggetto, solo attraverso un "protocollo di comunicazione definito". In linguaggi OO come Python questo può significare semplicemente "chiamare il metodo di un oggetto".
Doc Brown,

13

Concordo sul fatto che la prima definizione soddisfa i tre punti sollevati dal tuo insegnante. Non credo che abbiamo bisogno della parola chiave class per nulla. Sotto le coperte, cos'altro è un oggetto se non una struttura di dati con diversi tipi di dati e funzioni per lavorare con i dati? Naturalmente, anche le funzioni sono dati.

Andrei ancora oltre e direi che fare una programmazione orientata agli oggetti non dipende tanto dalle parole chiave fornite dal tuo linguaggio, puoi fare una programmazione orientata agli oggetti in C se lo desideri! In effetti, il kernel di Linux impiega tali tecniche.

Quello che puoi dedurre dalla parola chiave class qui è che il linguaggio fornisce supporto per questo tipo di costrutto pronto all'uso e che non è necessario attraverso tutti i cerchi per implementare di nuovo la funzionalità (che è un'attività piuttosto divertente in si!). Per non parlare di tutto lo zucchero sintattico che potresti ottenere.


che dire dell'eredità? Siamo cruciali sui sottotipi / supertipi in tempo reale? Il mio primo approccio potrebbe non intrattenere questo !!
scambio eccessivo del

5
L'ereditarietà non è richiesta in alcun modo per OOP. Potresti implementare l'ereditarietà anche nel tuo primo esempio. Non può essere molto "pulito" ma possibile lo stesso.
Zavior,

3
@Zavior quel commento mi fa pensare a VB6. Orientato agli oggetti senza ereditarietà rende davvero meno pulito il codice, per dirla in parole povere.
RubberDuck,

1
@overexchange Se ci pensate, l'ereditarietà consiste nella condivisione di codice / comportamento comuni tra le classi. Nulla ti impedisce di ripetere tutto quel codice per tutto il tempo. Sarebbe comunque terribile da mantenere. C'è una ragione per cui esiste l'eredità :)
Zavior,

1
@Zavior Nella sua forma più elementare "sottoclasse" è un'astrazione che dice "prima di restituire la funzione di invio e dati di ordine superiore che sto definendo qui (che pretendiamo sia una" classe "ah ah ha), creare un'istanza della funzione di invio e dati della superclasse a cui fa riferimento ThisParentFoo ". Questo è davvero tutto. Quando si tratta di ingenua eredità multipla, in realtà è ancora tutto ciò che è, ma con l'avvertenza che si introduce il "problema del diamante", motivo per cui l'ereditarietà multipla fa schifo.
zxq9,

9

Certo che puoi!

Il linguaggio di autoprogrammazione è un linguaggio dinamico orientato agli oggetti basato su prototipi in cui tutto è un oggetto e non ha senso delle classi o di alcun tipo. Si concentra sull'idea di oggetti prototipici e sull'idea di clonarli invece di avere classi come modelli di come creare oggetti.

Per maggiori informazioni, consultare http://www.selflanguage.org/ . Penso che sia molto interessante e se ti piace OOP è una buona idea controllare qualcosa che non è così comune.


0

Non sempre: dipende dalla lingua. Hai dimostrato la possibilità di farlo in Python ma (se la tua domanda è intesa come indipendente dalla lingua nonostante il tag Python) non tutte le lingue possono farlo. Java, ad esempio, per lo più non può. Ignorando la classe che contiene main, non è possibile definire metodi / campi arbitrari su un oggetto definito all'interno di main senza la parola chiave class. Sebbene esistano classi anonime, richiedono un'interfaccia e non possono avere membri pubblici tranne quelli definiti nell'interfaccia. Mentre è possibile definire interfacce personalizzate e quindi creare classi anonime per loro, questo è effettivamente lo stesso (ma meno conveniente) rispetto al semplice utilizzo di una classe.

Doc Brown ha un'ottima risposta, ma il punto che sto cercando di chiarire è che sono sicuro che almeno una lingua non consentirà affatto la tua soluzione.


Come principiante, per imparare il concetto di "Programmazione orientata agli oggetti", sì, sto cercando di essere indipendente dal linguaggio. Penso che "Doc Brown" abbia dato la stessa risposta, mi ha detto di leggere sicp text-chap3, che non ha nulla a che fare con la sintassi del linguaggio.
scambio eccessivo del

Vorrei nominare una lingua che richiede assolutamente l'uso delle lezioni per convalidare la mia risposta. Ma conosco solo poche lingue e purtroppo Java consente di aggirare il problema. C ++ ha strutture e Javascript flat out consente ciò che hai dimostrato. Ho il sospetto che Smalltalk ed Eiffel potrebbero aver bisogno di lezione poiché ho sentito che sono strettamente strutturati.
SkySpiral7,

Come Doc Brown, se avessi imparato oop usando lo schema, non avrei posto questa domanda. Sfortunatamente, la versione del corso SICP che sto imparando utilizza Python.
scambio eccessivo del

1
Ogni programma Java valido deve contenere la classparola chiave, quindi non è una sorpresa. Ma potresti assolutamente implementare il tuo sistema di oggetti in cima al sistema di oggetti Java, anche se non so perché tu voglia fare una cosa del genere.
Brian Gordon,

1. Java è davvero speciale in questo senso, dal momento che ha semplicemente eliminato tutte le altre parole chiave che potrebbero essere utilizzate per creare strutture di dati personalizzate. Quasi tutte le altre lingue che conosco hanno record o chiusure. 2. Anche in Java, è possibile programmare su una memoria creata da un array. E puoi implementare l'orientamento degli oggetti all'interno di questo, usando la classparola chiave solo perché la lingua richiede di mettere le tue funzioni in classi. Certo, questo è estremamente teorico, ma anche in Java puoi eseguire l'Orientamento degli oggetti senza le classi integrate!
cmaster

0

La definizione del tuo insegnante manca completamente del punto più importante della programmazione orientata agli oggetti, l'unica cosa che lo rende utile e unico. "Passaggio di messaggi" è un gruppo di sciocchezze inventate dalla gente di Smalltalk, ed è stato un fallimento ovunque sia stato provato. Il vero potere di OOP è qualcosa noto come sostituzione di Liskov e, sebbene il concetto sia abbastanza semplice da descrivere e comprendere, l'implementazione di base è abbastanza complessa da rendere sostanzialmente impossibile fare a meno del supporto a livello di linguaggio.

L'idea della sostituzione di Liskov è che dovunque il tuo codice si aspetti una variabile di un certo tipo, dovrebbe essere in grado di accettare qualsiasi tipo derivato da quel tipo e continuare a funzionare correttamente senza dover conoscere i dettagli del tipo derivato.

Ad esempio, i framework GUI utilizzano la sostituzione Liskov ovunque. Tendono ad avere una Controlclasse base che può rappresentare "qualsiasi controllo", che definisce un'interfaccia che conosce azioni di base come disegnare, ridimensionare e rispondere all'input dell'utente. Se si fa clic su un controllo, il framework dell'interfaccia utente chiamerà un Clickmetodo sul controllo senza preoccuparsi del tipo di controllo, quindi lasciare che il controllo gestisca il clic nel modo appropriato per la propria classe. Un Buttoncontrollo dovrebbe fare qualcosa di completamente diverso quando si fa clic su un TextBoxcontrollo, per fare solo un esempio.

Quindi sì, puoi creare qualcosa di simile agli oggetti usando il trucco delle funzioni nidificate sopra descritto, ma poiché non puoi ottenere l'ereditarietà e la sostituzione di Liskov in quel modo, è un sostituto estremamente limitato del vero OOP.


In linguaggio C, non posso dire "struct parent {}" e quindi "struct child {struct parent * ptr;}"? Questa non è ereditarietà nella sintassi del linguaggio senza oop?
Scambio eccessivo il

@overexchange: questo è un tentativo non OO di falsificarlo, ma il compilatore non ti permetterà di sostituirne uno con l'altro. (Non è possibile passare child*a una funzione che accetta un parent*come argomento, almeno non senza un typecast.) E ancora peggio, le strutture C non possono avere metodi associati a loro e non c'è supporto per i metodi virtuali , che sono ciò che fa funzionare la magia della sostituzione di Liskov, quindi devi costruire manualmente le VMT, che è un processo complicato che è facile da rovinare.
Mason Wheeler,

1
Il kernel di Linux utilizza un'emulazione di diverse tecniche OO, che devono essere tutte codificate manualmente senza supporto del linguaggio. Questo porta a molte opportunità per i bug che, essendo Linux, sono controbilanciati da un'applicazione liberale della Legge di Linus. Sì, è possibile farlo - l'equivalenza di Turing lo dimostra - ma il mio punto è estremamente difficile da ottenere senza supporto linguistico. Inoltre, perché tutte queste domande su C quando la domanda riguardava Python? In C non è possibile in primo luogo eseguire il trucco delle funzioni nidificate.
Mason Wheeler,

1
@overexchange Da quando Java è un "paradiso dei programmatori"?
Brandin,

1
Il passaggio di messaggi non è stato un errore nei sistemi OOP Smalltalk, Erlang o persino in stile Java in cui "messaggio" significa qualcosa di diverso da "chiamata di funzione" (segnali e slot di Qt con una coda thread-safe VS vecchio marketing Java che usa il termine "messaggio" quando significa "chiamata di metodo"). Messaggi! = Chiamate di funzione. La messaggistica autentica non ha solo successo, sembra essere l'unico modo che conosciamo per scrivere sistemi massicciamente concorrenti e robusti. Ciò è ortogonale all'implementazione di OOP in stile Java senza la parola chiave "class". Si può fare. E ' non è sempre utile. La messaggistica è fuori dal punto.
zxq9,

-1

Risposta breve veloce

Sì, i programmatori possono applicare la programmazione orientata agli oggetti senza "Classi".

Risposta descrittiva estesa e noiosa

Esistono diverse varianti di "Orientamento agli oggetti", il primo concetto che viene in mente a molti programmatori è "Classi".

Sì, i programmatori possono applicare la programmazione orientata agli oggetti senza "Classi", ma sono limitati alle funzionalità e alle limitazioni di ciascun linguaggio di programmazione.

Il tuo post è taggato come Python , quindi il titolo della domanda potrebbe essere più simile a "Come implementare la programmazione orientata agli oggetti senza classi in Python".

Attualmente uso la frase "Programmazione orientata a oggetti e classi", per identificare da altre varianti come "Prototyping" di Javascript, o Visual Basic "Basato", o emulazione in "Pure C" usando "funzioni".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.