Pouvez-vous implémenter une programmation «orientée objet» sans le mot-clé class?

29

Disons que nous voulons fournir une abstraction d'un "compte" dans une banque. Voici une approche, en utilisant un functionobjet en 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

Voici une autre approche utilisant l'abstraction de type (c'est-à-dire un classmot - clé en 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

Mon professeur a commencé le sujet "Programmation orientée objet" en introduisant le classmot - clé et en nous montrant ces puces:

Programmation orientée objet

Une méthode pour organiser des programmes modulaires:

  • Barrières d'abstraction
  • Passage de message
  • Regrouper les informations et les comportements associés

Pensez-vous que la première approche suffirait pour satisfaire la définition ci-dessus? Si oui, pourquoi avons-nous besoin du classmot - clé pour faire de la programmation orientée objet?

suréchange
la source
2
Content que vous soyez d'accord. =) Bien que je ne connaisse pas assez bien Python pour donner une réponse complète, vous pourriez être intéressé de savoir qu'en Javascript, la façon typique de faire de la POO est similaire à "l'objet fonction" que vous décrivez (bien que nous ayons également un héritage prototypique qui permet aux objets de "partager" les méthodes au lieu d'avoir des copies séparées de chaque méthode sur chaque objet; je suppose que Python classfait une optimisation similaire).
Ixrec
Si vous voulez une réponse détaillée, vous devriez poser une autre question ou rejoindre le salon de discussion, mais la réponse courte est (si vous ignorez complètement l'héritage prototypique, les tableaux, etc.) c'est fondamentalement vrai; la plupart des objets JS ne sont que des dictionnaires de clés de chaîne vers des valeurs arbitraires. foo.bar()est généralement identique à foo['bar'](), et en de rares occasions, cette dernière syntaxe est en fait utile.
Ixrec
8
C'est une question vraiment importante sur votre chemin vers une compréhension fondamentale de la POO. Si vous êtes intéressé, vous pouvez lire un de mes articles de blog où je crée un système d'objet simple en JavaScript sans compter sur aucune des parties OOP du langage. Votre premier exemple a une lacune importante: là où vous écririez object['method'](args), les objets Python font en fait l'équivalent de object['method'](object, args). Cela devient pertinent lorsqu'une classe de base appelle des méthodes dans une classe enfant, par exemple dans le modèle de stratégie.
amon
13
Comme d'autres l'ont noté, c'est une question perspicace sur la POO. J'en profite cependant pour noter que ce n'est pas du tout ainsi que les vraies banques représentent les comptes bancaires. Les banques n'ont pas d'objet "compte" mutable qui change lorsque vous le débitez et le créditez; ils ont une liste de transactions en écriture seule, puis calculent le solde à partir de la liste des transactions. Comme bon exercice, essayez d'implémenter ce mécanisme dans différentes langues.
Eric Lippert

Réponses:

66

Toutes nos félicitations! Vous avez redécouvert le fait bien connu que l'orientation d'objet peut se faire sans prise en charge spécifique du langage de programmation. C'est fondamentalement de la même manière que les objets sont introduits dans Scheme dans ce manuel classique . Notez que Scheme n'a pas de classmot - clé ou une sorte d'équivalent, et les objets peuvent être créés sans avoir même des classes.

Cependant, le paradigme orienté objet a connu un tel succès que de nombreux langages - et Python ne fait pas exception - lui fournissent un support intégré. Il s'agit simplement de faciliter l'utilisation du paradigme par les développeurs et de fournir une forme standard d'orientation d'objet pour ce langage. C'est essentiellement la même raison pour laquelle de nombreux langages fournissent une forboucle, bien qu'elle puisse être émulée en utilisant une whileboucle avec seulement une ou deux lignes de code supplémentaires - simplement une facilité d'utilisation .

Doc Brown
la source
"pour fournir une forme standard d'orientation d'objet pour ce langage" Est-ce que j'entends une critique de JavaScript là-dedans? ;)
jpmc26
1
@ jpmc26: pas intentionnellement. Et il semble qu'il existe des normes largement acceptées sur la façon dont les objets sont créés en JavaScript.
Doc Brown
@overexchange: avez-vous une question à poser?
Doc Brown
1
@overexchange: Eh bien, ce que signifie OOP est discutable, il existe différentes écoles de pensée, mais la définition du SICP est à peu près celle des 3 points de votre question. Il s'agit certainement de construire des abstractions, mais n'oubliez pas les points 2 et 3. Oui, le concept OOP englobe le "changement d'état", mais il permet également le concept "d'objets immuables" (comme la classe de chaîne en Java ou C #, Python contient également des types de données mutables et immuables). Et votre premier exemple dans votre question confirme cette définition ainsi que votre deuxième exemple.
Doc Brown
2
@overexchange: cela remonte à la définition d'Alain Kay de l'orientation d'objet (l'inventeur du langage de la conversation). Vous trouverez une réponse complète dans cet ancien article stackoverflow.com/questions/2347973/… SO. À mon humble avis, le "message passant entre des objets" au sens SICP signifie simplement de ne pas accéder directement aux données internes d'un objet, uniquement via un "protocole de communication défini". Dans les langages OO comme Python, cela peut simplement signifier "appeler la méthode d'un objet".
Doc Brown
13

Je conviens que la première définition satisfait les trois points soulevés par votre professeur. Je ne pense pas que nous ayons besoin du mot-clé class pour quoi que ce soit. Sous les couvertures, qu'est-ce qu'un objet, sinon une structure de données avec différents types de données et fonctions pour travailler avec les données? Bien sûr, les fonctions sont également des données.

J'irais même plus loin et dirais que faire de la programmation orientée objet ne dépend pas tellement des mots - clés fournis par votre langage, vous pouvez faire de la programmation orientée objet en C si vous le souhaitez! En fait, le noyau Linux utilise de telles techniques.

Ce que vous pouvez déduire du mot-clé class ici, c'est que le langage prend en charge ce type de construction hors de la boîte, et vous n'avez pas besoin de parcourir tous les cerceaux pour réimplémenter la fonctionnalité vous-même (ce qui est une tâche assez amusante dans lui-même!). Sans parler de tout le sucre syntaxique que vous pourriez également obtenir.

Zavior
la source
qu'en est-il de l'héritage? Sommes-nous cruciaux sur les sous-types / supertypes en temps réel? Ma première approche peut ne pas divertir cela !!
suréchange
5
L'héritage n'est en aucun cas requis pour la POO. Vous pouvez également implémenter l'héritage dans votre premier exemple. Il ne doit pas être très "propre" mais possible tout de même.
Zavior
3
@Zavior ce commentaire me fait penser à VB6. Orienté objet sans héritage rend en effet le code moins propre, pour le moins.
RubberDuck
1
@overexchange Quand on y pense, l'héritage consiste à partager du code / comportement commun entre les classes. Rien ne vous empêche de répéter tout ce code tout le temps. Ce serait super horrible à entretenir. Il y a une raison pour laquelle l'héritage existe :)
Zavior
1
@Zavior Dans sa forme la plus élémentaire, le "sous-classement" est une abstraction qui dit "avant de renvoyer la fonction de répartition et de données d'ordre supérieur que je définis ici (que nous prétendons être une" classe "ha ha ha), instancier la fonction de répartition et de données "superclasse" mentionnée par ThisParentFoo ". C'est vraiment tout. En ce qui concerne l'héritage multiple naïf, c'est toujours tout ce qu'il est, mais avec la mise en garde que vous introduisez le "problème du diamant", c'est pourquoi l'héritage multiple est nul.
zxq9
9

Bien sûr vous pouvez!

Le langage d'auto-programmation est un langage orienté objet basé sur un prototype dynamique dans lequel tout est un objet et il n'y a aucun sens des classes ou quoi que ce soit. Il est axé sur l'idée des objets prototypiques et l'idée de les cloner au lieu d'avoir des classes comme modèles de création d'objets.

Vous devriez consulter http://www.selflanguage.org/ pour plus d'informations. Je pense que c'est très intéressant et si vous aimez la POO, c'est une bonne idée de vérifier quelque chose qui n'est pas si courant.

DraQ
la source
0

Pas toujours: cela dépend de la langue. Vous avez démontré la capacité de le faire en Python mais (si votre question est destinée à être indépendante du langage malgré la balise Python), toutes les langues ne peuvent pas le faire. Java, par exemple, ne le peut généralement pas. En ignorant la classe qui contient main, il n'y a aucun moyen de définir des méthodes / champs arbitraires sur un objet défini dans main sans le mot-clé class. Bien que les classes anonymes existent, elles nécessitent une interface et elles ne peuvent avoir aucun membre public à l'exception de ceux définis dans l'interface. Bien qu'il soit possible de définir des interfaces personnalisées puis de leur créer des classes anonymes, c'est effectivement la même chose (mais moins pratique) que de simplement utiliser une classe.

Doc Brown a une excellente réponse mais le point que j'essaie de faire est que je suis sûr qu'il y a au moins une langue qui ne permettra pas du tout votre solution.

SkySpiral7
la source
En tant que débutant, pour apprendre le concept de "programmation orientée objet", oui j'essaie d'être indépendant du langage. Je pense que "Doc Brown" a donné une réponse sur les mêmes lignes, il m'a dit de lire sicp text-chap3, qui n'a rien à voir avec une syntaxe de langage.
suréchange
Je souhaiterais nommer une langue qui nécessite absolument l'utilisation de cours pour valider ma réponse. Mais je ne connais que quelques langages et hélas Java permet un contournement. C ++ a des structures et Javascript à plat permet ce que vous avez démontré. Je soupçonne que Smalltalk et Eiffel pourraient avoir besoin de cours puisque j'entends qu'ils sont strictement structurés.
SkySpiral7
En tant que Doc Brown, si j'avais appris oop en utilisant le schéma, je n'aurais pas posé cette question. Malheureusement, la version du cours SICP que j'apprends utilise python.
suréchange
1
Chaque programme Java valide doit contenir le classmot - clé, ce n'est donc pas vraiment une surprise. Mais vous pouvez absolument implémenter votre propre système d'objets par-dessus le système d'objets de Java, bien que je ne sache pas pourquoi vous voulez faire une telle chose.
Brian Gordon
1. Java est vraiment spécial à cet égard, car il a simplement jeté tous les autres mots clés qui pourraient être utilisés pour créer des structures de données personnalisées. Presque toutes les autres langues que je connais ont des enregistrements ou des fermetures. 2. Même en java, vous pouvez programmer sur une mémoire construite à partir d'un tableau. Et vous pouvez implémenter l'orientation objet à l'intérieur de cela, en utilisant le classmot - clé uniquement parce que le langage vous oblige à mettre vos fonctions en classes. Bien sûr, cela est extrêmement théorique, mais même en Java, vous pouvez faire l'Orientation Objet sans les classes intégrées!
cmaster
0

La définition de votre professeur manque complètement le point le plus important de la programmation orientée objet, la seule chose qui la rend utile et unique. "Message passant" est un tas de bêtises imaginé par les gens de Smalltalk, et c'est un échec partout où il a été essayé. La véritable puissance de la POO est quelque chose connue sous le nom de substitution Liskov , et bien que le concept soit assez simple à décrire et à comprendre, l'implémentation sous-jacente est suffisamment complexe pour qu'il soit essentiellement impossible de faire le bien sans le support au niveau du langage.

L'idée de la substitution Liskov est que partout où votre code attend une variable d'un certain type, il devrait pouvoir accepter n'importe quel type dérivé de ce type et fonctionner correctement sans avoir à connaître les détails du type dérivé.

Par exemple, les frameworks GUI utilisent la substitution Liskov partout. Ils ont tendance à avoir une Controlclasse de base qui peut représenter "n'importe quel contrôle", qui définit une interface qui connaît les actions de base telles que le dessin, le redimensionnement et la réponse aux entrées de l'utilisateur. Si vous cliquez sur un contrôle, le framework d'interface utilisateur appellera une Clickméthode sur le contrôle sans avoir à se soucier de quel type de contrôle il s'agit, puis laissera le contrôle gérer le clic de la manière appropriée pour sa propre classe. Un Buttoncontrôle doit faire quelque chose de complètement différent lorsqu'il est cliqué sur un TextBoxcontrôle, pour ne donner qu'un exemple.

Donc oui, vous pouvez créer quelque chose de similaire aux objets en utilisant l'astuce des fonctions imbriquées décrite ci-dessus, mais comme vous ne pouvez pas obtenir l'héritage et la substitution Liskov de cette façon, c'est un substitut extrêmement limité pour la vraie POO.

Mason Wheeler
la source
En langage C, ne puis-je pas dire 'struct parent {}' puis 'struct child {struct parent * ptr;}'? N'est-ce pas un héritage dans une syntaxe de langage non-oop?
suréchange
@overexchange: C'est une tentative non-OO de le simuler, mais le compilateur ne vous laissera pas remplacer l' un par l'autre. (Vous ne pouvez pas passer un child*à une fonction qui prend un parent*comme argument, du moins pas sans transtypage.) Et pire encore, les structures C ne peuvent pas avoir de méthodes liées, et il n'y a pas de support pour les méthodes virtuelles , qui sont ce qui fait que la magie de la substitution Liskov fonctionne, vous devez donc construire des VMT à la main, ce qui est un processus compliqué facile à visser.
Mason Wheeler
1
Le noyau Linux utilise une émulation de plusieurs techniques OO, qui doivent toutes être codées manuellement sans prise en charge linguistique. Cela conduit à de nombreuses opportunités de bugs, qui, étant Linux, sont contrebalancés par une application libérale de la loi de Linus. Oui, il est possible de le faire - l'équivalence de Turing le prouve - mais mon point de vue selon lequel il est extrêmement difficile de réussir sans le support de la langue est toujours valable. Aussi, pourquoi toutes ces questions sur C quand la question portait sur Python? En C, il n'est pas possible de faire le tour des fonctions imbriquées en premier lieu.
Mason Wheeler
1
@overexchange Depuis quand Java est-il un "paradis des programmeurs"?
Brandin
1
La transmission de messages n'a pas été un échec dans les systèmes OOP de type Smalltalk, Erlang ou même Java où "message" signifie quelque chose de différent de "appel de fonction" (signaux et emplacements de Qt avec une file d'attente threadsafe VS ancien marketing Java utilisant le terme "message" quand cela signifie "appel de méthode"). Messages! = Appels de fonction. La véritable messagerie n'est pas seulement un succès, elle semble être le seul moyen que nous connaissons pour écrire des systèmes massivement concurrents et robustes. Ceci est orthogonal à l'implémentation de la POO de style Java sans le mot clé 'class'. Cela peut être fait. Ce n'est pas toujours utile. La messagerie est hors de propos.
zxq9
-1

Réponse courte rapide

Oui, les programmeurs peuvent appliquer la programmation orientée objet sans "classes".

Réponse descriptive longue et ennuyeuse

Il existe plusieurs variantes de "l'Object Orientation", mais le premier concept qui vient à l'esprit de nombreux programmeurs est "Classes".

Oui, les programmeurs peuvent appliquer la programmation orientée objet sans "classes", mais sont limités aux fonctionnalités et limitations de chaque langage de programmation.

Votre message est étiqueté comme Python , par conséquent, le titre de votre question peut être plus comme "Comment implémenter la programmation orientée objet sans classes en Python".

J'utilise actuellement l'expression "Object and Class Oriented Programming", pour identifier à partir d'autres variantes comme "Prototyping" de Javascript, ou Visual Basic "Based", ou émulation en "Pure C" en utilisant "functors".

umlcat
la source