Können Sie eine objektorientierte Programmierung ohne das Schlüsselwort class implementieren?

29

Angenommen, wir möchten eine Abstraktion eines "Kontos" in einer Bank bereitstellen. Hier ist ein Ansatz, bei dem ein functionObjekt in Python verwendet wird:

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

Hier ist ein anderer Ansatz, der die Typabstraktion verwendet (z. B. classSchlüsselwort 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

Mein Lehrer begann das Thema "Objektorientierte Programmierung", indem er das classSchlüsselwort einführte und uns die folgenden Stichpunkte zeigte:

Objekt orientierte Programmierung

Eine Methode zur Organisation modularer Programme:

  • Abstraktionsbarrieren
  • Nachrichtenübergabe
  • Informationen und zugehöriges Verhalten bündeln

Glauben Sie, dass der erste Ansatz ausreichen würde, um die obige Definition zu erfüllen? Wenn ja, warum benötigen wir das classSchlüsselwort, um objektorientiert zu programmieren?

überaustausch
quelle
2
Froh, dass du damit einverstanden bist. =) Obwohl ich Python nicht gut genug kenne, um eine gründliche Antwort zu geben, könnte es Sie interessieren zu wissen, dass die typische Art der OOP-Ausführung in Javascript dem von Ihnen beschriebenen "Funktionsobjekt" ähnelt (obwohl wir auch eine prototypische Vererbung haben, die ermöglicht es den Objekten, Methoden zu "teilen", anstatt separate Kopien jeder Methode für jedes Objekt zu haben (ich gehe davon aus, dass Python classeine ähnliche Optimierung durchführt).
Ixrec
Wenn Sie eine detaillierte Antwort wünschen, sollten Sie eine andere Frage stellen oder dem Chatraum beitreten. Die kurze Antwort lautet jedoch (wenn Sie die prototypische Vererbung, Arrays usw. vollständig ignorieren), dass dies im Grunde wahr ist. Die meisten JS-Objekte sind nichts anderes als Wörterbücher mit Zeichenfolgenschlüsseln für beliebige Werte. foo.bar()ist normalerweise identisch mit foo['bar']()und in seltenen Fällen ist die letztere Syntax tatsächlich nützlich.
Ixrec
8
Dies ist eine wirklich wichtige Frage auf Ihrem Weg zu einem grundlegenden Verständnis von OOP. Wenn Sie interessiert sind, können Sie einen Blogbeitrag von mir lesen, in dem ich ein einfaches Objektsystem in JavaScript erstelle, ohne mich auf einen der OOP-Teile der Sprache zu verlassen. Ihr erstes Beispiel hat einen wichtigen Mangel: Wo Sie schreiben würden object['method'](args), tun Python-Objekte tatsächlich das Äquivalent von object['method'](object, args). Dies wird relevant, wenn eine Basisklasse Methoden in einer untergeordneten Klasse aufruft, z. B. im Strategy Pattern.
amon
13
Wie andere angemerkt haben, handelt es sich hierbei um eine perzeptive Frage zu OOP. Ich werde diese Gelegenheit jedoch nutzen, um festzustellen, dass echte Banken Bankkonten in keiner Weise darstellen. Banken haben kein veränderbares "Konto" -Objekt, das sich ändert, wenn Sie es belasten oder gutschreiben. Sie verfügen nur über eine schreibgeschützte Liste von Transaktionen und berechnen dann den Saldo aus der Liste der Transaktionen. Versuchen Sie als gute Übung, diesen Mechanismus in verschiedenen Sprachen zu implementieren.
Eric Lippert

Antworten:

66

Herzliche Glückwünsche! Sie haben die bekannte Tatsache wiederentdeckt, dass die Objektorientierung ohne spezielle Programmiersprachenunterstützung durchgeführt werden kann. Es ist im Grunde die gleiche Art und Weise, wie Objekte in diesem klassischen Lehrbuch in Schema eingeführt werden . Beachten Sie, dass für das Schema kein classSchlüsselwort oder eine Art Äquivalent vorhanden ist und dass Objekte ohne gleichmäßige Klassen erstellt werden können.

Das objektorientierte Paradigma war jedoch so erfolgreich, dass viele Sprachen - und Python ist keine Ausnahme - integrierte Unterstützung dafür bieten. Dies dient lediglich dazu, Entwicklern die Verwendung des Paradigmas zu erleichtern und eine Standardform der Objektorientierung für diese Sprache bereitzustellen. Es ist im Wesentlichen derselbe Grund, warum viele Sprachen eine forSchleife bereitstellen , obwohl sie mit einer whileSchleife mit nur ein oder zwei zusätzlichen Codezeilen emuliert werden kann - einfach benutzerfreundlich .

Doc Brown
quelle
"um eine Standardform der Objektorientierung für diese Sprache bereitzustellen" Höre ich dort eine Kritik an JavaScript? ;)
jpmc26
1
@ jpmc26: nicht absichtlich. Und es scheint, dass es einige allgemein akzeptierte Standards gibt, wie Objekte in JavaScript erstellt werden.
Doc Brown
@overexchange: Haben Sie eine Frage zu stellen?
Doc Brown
1
@overexchange: Nun, was OOP bedeutet, ist umstritten, es gibt verschiedene Denkrichtungen, aber die SICP-Definition ist so ziemlich diejenige der drei Stichpunkte in Ihrer Frage. Es geht definitiv darum, Abstraktionen zu erstellen, aber vergessen Sie nicht die Punkte 2 und 3. Ja, das OOP-Konzept umfasst "Zustandsänderung", erlaubt aber auch das Konzept "unveränderlicher Objekte" (wie die Zeichenfolgenklasse in Java oder C #, Python) hat einige veränderbare und einige unveränderbare Datentypen). Und Ihr erstes Beispiel in Ihrer Frage bestätigt diese Definition ebenso wie Ihr zweites Beispiel.
Doc Brown
2
@overexchange: Dies geht zurück auf Alain Kays Definition der Objektorientierung (der Erfinder der Small-Talk-Sprache). Eine umfassende Antwort finden Sie in diesem früheren SO-Artikel unter stackoverflow.com/questions/2347973/… . IMHO "Message Passing Between Objects" im Sinne von SICP bedeutet lediglich, nicht direkt auf die inneren Daten eines Objekts zuzugreifen, sondern nur über ein "definiertes Kommunikationsprotokoll". In OO-Sprachen wie Python kann dies nur "Methode eines Objekts aufrufen" bedeuten.
Doc Brown
13

Ich würde zustimmen, dass die erste Definition die drei Punkte erfüllt, die Ihr Lehrer gemacht hat. Ich glaube nicht, dass wir das Schlüsselwort class für irgendetwas brauchen . Was ist ein anderes Objekt als eine Datenstruktur mit unterschiedlichen Datentypen und Funktionen, um mit den Daten zu arbeiten? Natürlich sind die Funktionen auch Daten.

Ich würde sogar noch weiter gehen und sagen, dass objektorientiertes Programmieren nicht so sehr von den Schlüsselwörtern abhängt, die Ihre Sprache bereitstellt. Sie können objektorientiertes Programmieren in C durchführen, wenn Sie möchten! Tatsächlich verwendet der Linux-Kernel solche Techniken.

Was Sie dem Schlüsselwort class entnehmen können, ist, dass die Sprache diese Art von Konstrukt von Anfang an unterstützt, und dass Sie nicht alle Schritte durchlaufen müssen, um die Funktionalität selbst erneut zu implementieren (was eine ziemlich unterhaltsame Aufgabe ist) selbst!). Ganz zu schweigen von dem syntaktischen Zucker, den Sie vielleicht auch bekommen.

Verhalten
quelle
was ist mit Vererbung? Sind wir für Subtypen / Supertypen in Echtzeit von entscheidender Bedeutung? Mein erster Ansatz kann dies nicht unterhalten !!
Überaustausch
5
Vererbung ist für OOP in keiner Weise erforderlich. Sie können die Vererbung auch in Ihrem ersten Beispiel implementieren. Es kann nicht sehr "sauber" sein, aber trotzdem möglich.
Verhalten
3
@ Verhalten dieser Kommentar lässt mich an VB6 denken. Objektorientiert ohne Vererbung ist in der Tat weniger sauberer Code, um es milde auszudrücken.
RubberDuck
1
@overexchange Bei der Vererbung geht es darum, gemeinsamen Code / Verhalten zwischen Klassen zu teilen. Nichts hindert Sie daran, den gesamten Code ständig zu wiederholen. Wäre aber super schrecklich zu pflegen. Es gibt einen Grund, warum Vererbung existiert :)
Verhalten
1
@Zavior In seiner grundlegendsten Form ist "Unterklasse" eine Abstraktion, die besagt, "bevor Sie die Dispatch- und Daten-habende Funktion höherer Ordnung zurückgeben", die ich hier definiere (was wir als "Klasse" bezeichnen wollen, ha ha ha) instanziieren Sie die von ThisParentFoo genannte "Superklasse" Versand- und Datenfunktion. " Das ist wirklich alles was es ist. Wenn es um naive Mehrfachvererbung geht, ist das eigentlich immer noch alles, aber mit der Einschränkung, dass Sie das "Diamantproblem" einführen, ist das der Grund, warum Mehrfachvererbung scheiße ist.
zxq9
9

Natürlich kannst du!

Die Programmiersprache Self ist eine dynamische, prototypbasierte, objektorientierte Sprache, in der alles ein Objekt ist und es keinerlei Sinn für Klassen oder Ähnliches gibt. Es konzentriert sich auf die Idee, prototypische Objekte zu erstellen und diese zu klonen, anstatt Klassen als Vorlagen für die Erstellung von Objekten zu verwenden.

Weitere Informationen erhalten Sie unter http://www.selflanguage.org/ . Ich finde es sehr interessant und wenn Sie OOP mögen, ist es eine gute Idee, etwas zu überprüfen, das nicht so häufig vorkommt.

DraQ
quelle
0

Nicht immer: es kommt auf die Sprache an. Sie haben die Fähigkeit demonstriert, dies in Python zu tun, aber (wenn Ihre Frage trotz des Python-Tags sprachunabhängig sein soll), können dies nicht alle Sprachen. Java zum Beispiel kann es meistens nicht. Wenn Sie die Klasse ignorieren, die main enthält, können Sie keine beliebigen Methoden / Felder für ein in main definiertes Objekt ohne das Schlüsselwort class definieren. Obwohl anonyme Klassen vorhanden sind, benötigen sie eine Schnittstelle und können keine öffentlichen Mitglieder außer den in der Schnittstelle definierten haben. Es ist zwar möglich, benutzerdefinierte Schnittstellen zu definieren und dann anonyme Klassen für sie zu erstellen, dies ist jedoch praktisch das Gleiche (aber weniger bequem) als die Verwendung einer Klasse.

Doc Brown hat eine großartige Antwort, aber der Punkt, den ich versuche, ist, dass ich sicher bin, dass es mindestens eine Sprache gibt, die Ihre Lösung überhaupt nicht zulässt.

SkySpiral7
quelle
Als Anfänger, um das Konzept der "objektorientierten Programmierung" zu erlernen, versuche ich, sprachunabhängig zu sein. Ich denke, "Doc Brown" hat in den gleichen Zeilen geantwortet, er hat mir gesagt, sicp text-chap3 zu lesen, was mit keiner Sprachsyntax zu tun hat.
Überaustausch
Ich wünschte, ich würde eine Sprache benennen, die es unbedingt erfordert, Klassen zu verwenden, um meine Antwort zu validieren. Aber ich kenne nur ein paar Sprachen und leider erlaubt Java eine Umgehung. C ++ hat Strukturen und Javascript erlaubt genau das, was Sie demonstriert haben. Ich vermute, Smalltalk und Eiffel benötigen möglicherweise Unterricht, da ich höre, dass sie streng strukturiert sind.
SkySpiral7
Als Doc Brown hätte ich diese Frage nicht gestellt, wenn ich gelernt hätte, das Schema zu verwenden. Leider verwendet die Version des SICP-Kurses, die ich lerne, Python.
Überaustausch
1
Jedes gültige Java-Programm muss das classSchlüsselwort enthalten, daher ist dies keine große Überraschung. Aber Sie könnten durchaus Ihr eigenes Objektsystem auf Javas Objektsystem implementieren, obwohl ich nicht weiß, warum Sie so etwas tun wollen.
Brian Gordon
1. Java ist in dieser Hinsicht wirklich besonders, da es einfach alle anderen Schlüsselwörter weggeworfen hat, die zum Erstellen benutzerdefinierter Datenstrukturen verwendet werden könnten. Fast alle anderen Sprachen, die ich kenne, haben entweder Aufzeichnungen oder Abschlüsse. 2. Auch in Java, Sie können auf einem aus einem Array eingebauten Speicher programmieren. Und Sie können die Objektorientierung darin implementieren, indem Sie nur das classSchlüsselwort verwenden, da die Sprache erfordert, dass Sie Ihre Funktionen in Klassen einteilen. Das ist natürlich extrem theoretisch, aber auch in Java können Sie die Objektorientierung ohne die eingebauten Klassen durchführen!
cmaster
0

Die Definition Ihres Lehrers geht völlig am wichtigsten Punkt der objektorientierten Programmierung vorbei, was sie nützlich und einzigartig macht. "Message Passing" ist ein Haufen Unsinn, den sich die Smalltalk-Leute ausgedacht haben, und er ist überall dort gescheitert, wo es versucht wurde. Die wahre Stärke von OOP ist die sogenannte Liskov-Substitution . Das Konzept ist zwar recht einfach zu beschreiben und zu verstehen, die zugrunde liegende Implementierung ist jedoch so komplex, dass es ohne die Unterstützung auf Sprachebene im Wesentlichen unmöglich ist, richtig vorzugehen.

Die Idee der Liskov-Substitution ist, dass überall dort, wo Ihr Code eine Variable eines bestimmten Typs erwartet, jeder von diesem Typ abgeleitete Typ akzeptiert und trotzdem korrekt funktioniert, ohne dass Sie die Details des abgeleiteten Typs kennen müssen.

Beispielsweise verwenden GUI-Frameworks überall die Liskov-Substitution. Sie verfügen in der Regel über eine Basisklasse Control, die "ein beliebiges Steuerelement" darstellen kann. Dabei handelt es sich um eine Schnittstelle, die grundlegende Aktionen wie Zeichnen, Ändern der Größe und Reagieren auf Benutzereingaben kennt. Wenn Sie auf ein Steuerelement klicken, ruft das UI-Framework eine ClickMethode für das Steuerelement auf, ohne sich darum kümmern zu müssen, um welche Art von Steuerelement es sich handelt, und überlässt es dem Steuerelement, den Klick in der für seine Klasse geeigneten Weise zu behandeln. Ein ButtonSteuerelement sollte beim Anklicken etwas völlig anderes tun als ein TextBoxSteuerelement, um nur ein Beispiel zu nennen.

Also ja, Sie können mit dem oben beschriebenen Trick mit verschachtelten Funktionen etwas Ähnliches wie Objekte erstellen, aber da Sie auf diese Weise keine Vererbung und Liskov-Substitution erhalten können, ist dies ein äußerst begrenzter Ersatz für echtes OOP.

Mason Wheeler
quelle
Kann ich in C nicht 'struct parent {}' und dann 'struct child {struct parent * ptr;}' sagen? Ist dies keine Vererbung in der Non-Oop-Sprachsyntax?
Überaustausch
@overexchange: Das ist ein Nicht-OO-Versuch, es zu fälschen, aber der Compiler lässt nicht zu, dass Sie eines durch das andere ersetzen . (Sie können eine nicht child*an eine Funktion übergeben, die a parent*als Argument verwendet, zumindest nicht ohne Typumwandlung.) Schlimmer noch, an C-Strukturen können keine Methoden gebunden sein, und virtuelle Methoden werden nicht unterstützt Was die Magie der Liskov-Substitution ausmacht, ist, dass Sie VMTs von Hand konstruieren müssen. Dies ist ein komplizierter Prozess, der sich leicht vermasseln lässt.
Mason Wheeler
1
Der Linux-Kernel verwendet eine Emulation mehrerer OO-Techniken, die alle ohne Sprachunterstützung manuell codiert werden müssen. Dies führt zu zahlreichen Möglichkeiten für Bugs, denen als Linux eine liberale Anwendung des Linus-Gesetzes entgegenwirkt. Ja, das ist möglich - Turing Equivalence beweist dies -, aber mein Punkt, dass es extrem schwierig ist, ohne Sprachunterstützung richtig zu kommen, bleibt bestehen. Warum all diese Fragen zu C, wenn die Frage zu Python war? In C ist es nicht möglich, die verschachtelten Funktionen überhaupt auszuführen.
Mason Wheeler
1
@overexchange Seit wann ist Java ein Paradies für Programmierer?
Brandin
1
Das Weiterleiten von Nachrichten war in Smalltalk-, Erlang- oder sogar Java-OOP-Systemen kein Fehler, bei denen "Nachricht" etwas anderes als "Funktionsaufruf" bedeutet (Qt-Signale und -Slots mit einer thread-sicheren Warteschlange im Vergleich zu altem Java-Marketing mit dem Begriff "Nachricht"). wenn es "Methodenaufruf" bedeutet). Nachrichten! = Funktionsaufrufe. Echtes Messaging ist nicht nur erfolgreich, es scheint auch die einzige Möglichkeit zu sein, massiv parallele und robuste Systeme zu schreiben. Dies ist orthogonal zur Implementierung von OOP im Java-Stil ohne das Schlüsselwort 'class'. Es kann getan werden. Es ist nicht immer nützlich. Messaging ist nebensächlich.
zxq9
-1

Schnelle kurze Antwort

Ja, Programmierer können die objektorientierte Programmierung ohne "Klassen" anwenden.

Langweilige ausführliche beschreibende Antwort

Es gibt verschiedene Variationen von "Objektorientierung". Das erste Konzept, das vielen Programmierern einfällt, ist "Klassen".

Ja, Programmierer können die objektorientierte Programmierung ohne "Klassen" anwenden, sind jedoch auf die Funktionen und Einschränkungen der einzelnen Programmiersprachen beschränkt.

Ihr Beitrag ist mit Python markiert , daher lautet Ihr Fragentitel möglicherweise eher "So implementieren Sie objektorientierte Programmierung ohne Klassen in Python".

Ich verwende derzeit die Phrase "Objekt- und klassenorientierte Programmierung", um anhand anderer Variationen wie Javascripts "Prototyping" oder Visual Basic "Based" oder der Emulation in "Pure C" mithilfe von "Funktoren" zu identifizieren.

umlcat
quelle