Dies wird eine sehr nicht-technische, weiche Frage sein, und ich bin mir nicht sicher, ob dies die richtige Plattform ist. Aber ich bin ein beginnender CS-Student, also hoffe ich, dass ihr es toleriert.
Im ersten Semester wurden wir mit OOP-Konzepten wie Kapselung, Verstecken von Daten, Modularität, Vererbung usw. über Java und UML vertraut gemacht. (Java ist meine erste Programmiersprache)
So wie ich es verstehe, ist OOP eine Möglichkeit, die Komplexität von Software zu managen. Die Prinzipien sind jedoch nicht neu oder einzigartig, sondern in gewisser Weise universell für alle technischen Bereiche.
Beispielsweise ist ein Auto eine sehr komplexe Struktur, deren Komplexität durch eine Hierarchie modularer und gekapselter Komponenten mit genau definierten Verhaltensweisen und Schnittstellen gesteuert wird.
Den Grund für die Einführung eines neuen Programmierparadigmas verstehe ich jedoch nicht. Ich denke, dass alle Prinzipien, die zum Verwalten der Komplexität verwendet werden, durch prozedurale Programmiersprachen realisiert werden können. Zum Beispiel können wir das Programm aus Gründen der Modularität einfach in viele kleine Programme aufteilen, die genau definierte Aufgaben ausführen, deren Code in separaten Dateien enthalten ist. Diese Programme würden durch ihre wohldefinierte Eingabe und Ausgabe miteinander interagieren. Die Dateien können geschützt (verschlüsselt?) Sein, um eine Kapselung zu erreichen. Zur Wiederverwendung von Code können wir diese Dateien nur aufrufen, wenn sie in neuen Programmen benötigt werden. Erfasst das nicht alles, was OOP ist, oder vermisse ich etwas sehr Offensichtliches?
Ich verlange keinen Beweis dafür, dass OOP mit Komplexität umgeht. Meiner Meinung nach schon. Aber ich denke, dass alle Prinzipien, die zur Verwaltung der Komplexität verwendet werden, wie Modularität, Kapselung, Verstecken von Daten usw., von prozeduralen Sprachen sehr einfach implementiert werden können. Warum also wirklich OOP, wenn wir ohne Komplexität auskommen?
quelle
Antworten:
Lass es mich mit einer wirklich niedrigen theoretischen Antwort versuchen :)
Was Sie wirklich fragen, ist: Warum sollte die Unterstützung der Objektorientierung direkt in die Sprache aufgenommen werden, wenn prozedurale Sprachen zum Entwerfen und Schreiben von OO-Code verwendet werden können?
Und die Antwort lautet: Einen Standard dafür zu haben, wie OO im Quellcode ausgedrückt wird, damit Sie nicht mit 22 verschiedenen Implementierungen für dieselbe Abstraktion enden.
Angenommen, ich erstelle ein
MagicButton
und einMagicSlider
, das in einem Benutzeroberflächensystem verwendet werden kann. Ich brauche eine Möglichkeit, die Methoden, die mit dem MagicButton verwendet werden können, die Methoden, die nur mit dem MagicSlider verwendet werden können, und die Methoden, die von beiden verwendet werden können, zu gruppieren. Diese Objekte haben einige gemeinsame Methoden, da sie beide Magic-GUI-Objekte sind.Ich kann die Gruppierung durchführen, indem ich Funktionen auf eine spezielle Art
MagicSlider_DoSomething ...
und Weise benenne , indemMagicSliderMethods.XXX
ich die Methoden in bestimmte Dateien einbinde, die auf eine spezielle Art und Weise benannt wurden , oder ich könnte eine andere spezielle Art und Weise finden, um dasselbe zu tun. Wenn es in der Sprache keinen Standard gibt, werde ich es anders machen als Sie und anders als alle anderen. Dies erschwert das Teilen von Code erheblich.Ja, der späte Versand - virtuelle Methoden in OO-Sprachen - kann in prozeduralen Sprachen implementiert werden, aber es gibt so viele verschiedene Möglichkeiten, dies zu implementieren. Je nachdem, wer den Code geschrieben hat, werden Sie unterschiedliche Implementierungen von OO im selben Programm haben.
Denken Sie an den schlechten Wartungsentwickler. Diese Person muss unterschiedliche Objektabstraktionen und unterschiedliche Methoden zum Aufrufen virtueller Methoden verwalten, je nachdem, wer den ursprünglichen Code geschrieben hat.
Außerdem: Mit den Abstraktionen in der Sprache können fortgeschrittene Code-Editoren wie Eclipse den Code statisch analysieren. Beispielsweise kann Eclipse eine Liste aller Methoden anbieten, die für ein Objekt verwendet werden können, sowie die automatische Implementierung leerer "TODO-Methoden". Eclispe weiß genau, welche Methoden Ihre Klasse basierend auf den von Ihnen erweiterten Klassen und den von Ihnen implementierten Schnittstellen implementieren muss. Dies wäre fast unmöglich, wenn es keinen Sprachstandard für OO gäbe.
quelle
Keines davon sind OOP-Konzepte. Sie existieren alle außerhalb von OO, unabhängig von OO und viele wurden sogar vor OO erfunden.
Wenn Sie also der Meinung sind, dass OO genau das ist, dann ist Ihre Schlussfolgerung richtig: Sie können all dies in prozeduralen Sprachen tun , weil sie nichts mit OO zu tun haben .
Eine der bahnbrechenden Arbeiten zum Thema Modularität befasst sich beispielsweise mit den Kriterien für die Zerlegung von Systemen in Module . Es gibt dort keine Erwähnung von OO. (Es wurde 1972 geschrieben, bis dahin war OO immer noch eine dunkle Nische, obwohl es bereits mehr als ein Jahrzehnt alt war.)
Während Datenabstraktion in OO wichtig ist, ist sie eher eine Folge der Hauptfunktion von OO (Messaging) als eine definierende Funktion. Es ist auch sehr wichtig, sich daran zu erinnern, dass es verschiedene Arten der Datenabstraktion gibt. Die beiden am häufigsten verwendeten Arten der Datenabstraktion (wenn wir "überhaupt keine Abstraktion" ignorieren, die wahrscheinlich immer noch häufiger verwendet wird als die beiden anderen zusammen) sind abstrakte Datentypen und Objekte . Wenn Sie also "Information Hiding", "Encapsulation" und "Data Abstraction" sagen, haben Sie nichts über OO gesagt, da OO nur eine Form der Datenabstraktion ist und die beiden sich in der Tat grundlegend unterscheiden:
Dies bedeutet im Übrigen, dass Klassen in Java nicht objektorientiert sind. Zwei Instanzen derselben Klasse können auf die Repräsentation und die private Implementierung des jeweils anderen zugreifen. Daher sind Instanzen von Klassen keine Objekte, sondern ADT-Instanzen. Java
interface
s, aber sie bieten objektorientierte Datenabstraktion. Mit anderen Worten: Nur Instanzen von Interfaces sind Objekte in Java, Instanzen von Klassen nicht.Grundsätzlich können Sie für Typen nur Schnittstellen verwenden. Dies bedeutet, dass Parametertypen von Methoden und Konstruktoren, Rückgabetypen von Methoden, Typen von Instanzfeldern, statischen Feldern und lokalen Feldern, das Argument für einen
instanceof
Operator oder einen Umwandlungsoperator und die Typargumente für einen generischen Typkonstruktor immer Schnittstellen sein müssen. Eine Klasse darf nur direkt nach demnew
Operator verwendet werden, nirgendwo anders.Was Sie beschreiben, ist OO.
Das ist in der Tat eine gute Möglichkeit, über OO nachzudenken. Genau das haben sich die ursprünglichen Erfinder von OO vorgenommen. (Alan Kay ging noch einen Schritt weiter: Er stellte sich viele kleine Computer vor, die sich gegenseitig Nachrichten über das Netzwerk senden.) Was Sie als "Programm" bezeichnen, wird normalerweise als "Objekt" bezeichnet, und statt "Aufrufen" sagen wir normalerweise "Nachricht senden" ".
Bei der Objektorientierung dreht sich alles um Messaging (auch bekannt als dynamischer Versand ). Der Begriff "Objektorientiert" wurde von Dr. Alan Kay, dem Hauptdesigner von Smalltalk, geprägt und definiert ihn folgendermaßen :
Lassen Sie uns das zusammenfassen:
In Bezug auf die Implementierung ist Messaging ein spät gebundener Prozeduraufruf. Wenn Prozeduraufrufe spät gebunden sind, können Sie zur Entwurfszeit nicht wissen, was Sie aufrufen werden, sodass Sie keine Annahmen über die konkrete Darstellung des Status treffen können. Eigentlich geht es also um Messaging. Late-Binding ist eine Implementierung von Messaging, und Encapsulation ist eine Folge davon.
Später stellte er klar, dass " die große Idee" Messaging "ist, und bedauert, sie" objektorientiert "statt" nachrichtenorientiert "genannt zu haben, weil der Begriff" objektorientiert "den Fokus auf das Unwichtige (Objekte) legt ) und lenkt von dem ab, was wirklich wichtig ist (Messaging):
(Natürlich konzentrieren sich die meisten Menschen heute nicht mehr auf Objekte, sondern auf Klassen, was noch falscher ist.)
Messaging ist für OO sowohl als Metapher als auch als Mechanismus von grundlegender Bedeutung .
Wenn Sie jemandem eine Nachricht senden, wissen Sie nicht, was er damit macht. Das einzige, was Sie beobachten können, ist ihre Antwort. Sie wissen nicht, ob sie die Nachricht selbst verarbeitet haben (dh ob das Objekt eine Methode hat), ob sie die Nachricht an eine andere Person weitergeleitet haben (Delegation / Proxying) oder ob sie sie überhaupt verstanden haben. Darum geht es bei der Verkapselung, darum geht es bei OO. Sie können einen Proxy nicht einmal von der Realität unterscheiden, solange er so reagiert, wie Sie es erwarten.
Ein "moderner" Begriff für "Messaging" ist "dynamischer Methodenversand" oder "virtueller Methodenaufruf", der jedoch die Metapher verliert und sich auf den Mechanismus konzentriert.
Es gibt also zwei Möglichkeiten, die Definition von Alan Kay zu betrachten: Wenn Sie sich die Definition einzeln ansehen, stellen Sie möglicherweise fest, dass Messaging im Grunde genommen ein späteingebundener Prozeduraufruf ist und eine späte Bindung eine Verkapselung impliziert und # 2 sind eigentlich überflüssig, und OO dreht sich alles um spätes Binden.
Später stellte er jedoch klar, dass das Wichtigste das Versenden von Nachrichten ist, sodass wir es aus einem anderen Blickwinkel betrachten können: Nachrichtenversenden ist verspätet. Wenn Messaging das einzig mögliche wäre, dann wäre # 3 trivial wahr: Wenn es nur eine Sache gibt und diese Sache verspätet gebunden ist, dann sind alle Dinge verspätet gebunden. Die Verkapselung ergibt sich wiederum aus dem Messaging.
Ähnliche Punkte finden sich auch in On Understanding Data Abstraction, überarbeitet von William R. Cook, und in seinem Vorschlag für vereinfachte, moderne Definitionen von "Objekt" und "objektorientiert" :
In Smalltalk-72 gab es nicht einmal Gegenstände! Es gab nur Nachrichtenströme, die analysiert, umgeschrieben und umgeleitet wurden. Zuerst kamen Methoden (Standardmethoden zum Parsen und Umleiten der Nachrichtenströme), später kamen Objekte (Gruppierungen von Methoden, die einen privaten Status gemeinsam haben). Die Vererbung erfolgte viel später, und Klassen wurden nur eingeführt, um die Vererbung zu unterstützen. Wäre Kays Forschungsgruppe bereits über Prototypen informiert gewesen, hätte sie wahrscheinlich niemals Klassen eingeführt.
Benjamin Pierce in Types and Programming Languages argumentiert, dass das bestimmende Merkmal der Objektorientierung die offene Rekursion ist .
Also: Laut Alan Kay dreht sich bei OO alles um Messaging. Laut William Cook dreht sich bei OO alles um den Versand dynamischer Methoden (was eigentlich dasselbe ist). Laut Benjamin Pierce dreht sich bei OO alles um Open Recursion, was im Grunde bedeutet, dass Selbstreferenzen dynamisch aufgelöst werden (oder zumindest so), oder mit anderen Worten, Messaging.
Wie Sie sehen, hat die Person, die den Begriff "OO" geprägt hat, eine eher metaphysische Sicht auf Objekte, Cook eine eher pragmatische Sicht und Pierce eine sehr rigorose mathematische Sicht. Aber das Wichtige ist: Der Philosoph, der Pragmatiker und der Theoretiker sind sich einig! Messaging ist die eine Säule von OO. Zeitraum.
Beachten Sie, dass hier keine Vererbung erwähnt wird! Vererbung ist für OO nicht wesentlich. Im Allgemeinen haben die meisten OO-Sprachen eine Möglichkeit zur Wiederverwendung der Implementierung, dies muss jedoch nicht unbedingt eine Vererbung sein. Dies kann beispielsweise auch eine Form der Delegation sein. Tatsächlich wird im Vertrag von Orlando die Delegation als Alternative zur Vererbung erörtert und wie unterschiedliche Formen der Delegation und Vererbung zu unterschiedlichen Entwurfspunkten im Entwurfsraum objektorientierter Sprachen führen. (Beachten Sie, dass die Leute auch in Sprachen, die Vererbung unterstützen, wie Java, tatsächlich lernen, dies zu vermeiden, was wiederum darauf hinweist, dass dies für OO nicht erforderlich ist.)
quelle
Wenn Sie "sehr leicht" sagen, geben Sie eine sehr mutige Aussage ab. Ich lese es so: "Ich sehe die Schwierigkeit nicht, sie darf also nicht sehr groß sein." Wenn Sie das so formulieren, wird klar, dass Sie nicht fragen: "Warum brauchen wir OO?", Sondern: "Warum sind die Schwierigkeiten, auf die andere Programmierparadigmen gestoßen sind, die zur Erfindung von OO geführt haben, für mich nicht sofort ersichtlich?" "
Eine Antwort auf diese Frage ist, dass viele dieser Schwierigkeiten bei den Programmen, an denen Sie arbeiten, nicht existieren. Sie werden nicht aufgefordert, den 40 Jahre alten Spaghetti-Code zu aktualisieren. Sie versuchen nicht, einen neuen Anzeigemanager für ein Betriebssystem zu schreiben. Sie debuggen keine verteilten Multithread-Anwendungen.
Für viele der Arten von Spielzeugprogrammen, mit denen wir CS-Studenten beauftragt sind, können wir sie genauso gut in BASIC oder Assembly schreiben wie Java oder Python. Das liegt daran, dass die inhärente Komplexität der Aufgaben so gering ist, dass es nur einen Entwickler gibt, keine älteren Interoperabilitätsprobleme gibt, die Leistung keine Rolle spielt und der Code wahrscheinlich immer nur eine Handvoll Mal auf einem Computer ausgeführt wird.
Stellen Sie sich vor, Sie nehmen einen studentischen Fahrer und bitten ihn, in der Hauptverkehrszeit mit einem Schaltgetriebe ohne Synchronisierung auf eine belebte Straße zu fahren und einen steilen Hügel hinaufzufahren. Katastrophe. Warum? Sie sind nicht in der Lage, den Komplexitätsgrad zu verwalten, der erforderlich ist, um gleichzeitig alle für die Aufgabe erforderlichen Regeln einzuhalten.
Stellen Sie sich jetzt denselben Studenten und dasselbe Fahrzeug vor, wie sie auf einem leeren Parkplatz im Schritttempo fahren. Sie sind in Ordnung, weil ihr Können der Aufgabe angemessen ist. Es besteht kein Druck, es besteht nur ein geringes Risiko, und sie können die einzelnen Teilaufgaben Anfahren, Erfassen, Schalten, Beschleunigen und Lenken nacheinander übernehmen.
Dieser Student könnte fragen, warum wir Automatikgetriebe haben, wenn ein erfahrener Fahrer all diese Dinge gleichzeitig tun kann? Die Antwort ist, dass ein ausreichend erfahrener Fahrer unter optimalen Bedingungen keine Automatik benötigt. Wir sind jedoch nicht alle Profifahrer in Topform, und wir möchten in der Regel, dass die Konstrukteure des Autos sich um all diese Komplexität kümmern.
Ein erfahrener, disziplinierter Programmierer kann in der Tat ein funktionierendes System mit hoher Komplexität in C oder Assembly erstellen. Aber wir sind nicht alle Linus Torvalds. Wir sollten es auch nicht müssen, um nützliche Software zu erstellen.
Ich persönlich habe kein Interesse daran, alle Merkmale einer modernen Sprache neu erfinden zu müssen, bevor ich überhaupt auf das vorliegende Problem eingehen kann. Wenn ich eine Sprache nutzen kann, die Lösungen für bereits gelöste Probleme enthält, warum sollte ich das dann nicht tun?
Also werde ich Ihre Frage umdrehen und Sie fragen, warum wir sie nicht verwenden sollten, wenn Sprachen praktische Funktionen wie Verkapselung und Polymorphismus bieten?
quelle
Was Sie beschreiben, ist nicht OOP, es ist Abstraktion. Abstraktion ist in allen modernen Designmodellen vorhanden, auch in solchen, die keine OOP sind. Und OOP ist eine sehr spezifische Art von Abstraktion.
Erstens ist es erwähnenswert, dass es keine einheitliche Definition von OOP gibt, so dass es Menschen geben kann, die mit dem, was ich als OOP bezeichne, nicht einverstanden sind.
Zweitens ist es wichtig, sich daran zu erinnern, dass OOP von traditionellen Designmodellen inspiriert wurde, sodass die Ähnlichkeiten mit dem Autodesign kein Zufall sind.
Es gibt jedoch einige Möglichkeiten, wie OOP differenzierter ist als das, was Sie gesagt haben:
Kapselung: Es geht nicht nur darum, eine festgelegte Schnittstelle für ein Modul (dh Abstraktion) zu haben, sondern auch darum, den Zugriff über diese Schnittstelle hinaus zu verbieten. In Java ist der Zugriff auf eine private Variable ein Kompilierungsfehler, während Sie in Ihrem Fahrzeugdesign (in einigen Fällen) Dinge auf eine Weise verwenden können, die sich von der beabsichtigten Schnittstelle unterscheidet.
Vererbung: Dies ist wirklich das, was OOP einzigartig macht. Sobald Sie eine Schnittstelle definiert haben, können Sie mehrere Dinge zur Umsetzung dieser Schnittstelle machen, und Sie können in einem hierarchischen Weg , dies zu tun, bestimmte Teile ihrer Umsetzung zu verändern, während erben all bisherigen Teile, massiv Code - Duplizierung zu reduzieren.
Wenn Sie in den gekapselten Komponenten eines Autos denken, gibt es nicht wirklich ein Äquivalent dazu. Es gibt für mich keine Möglichkeit, ein Zahnrad herzustellen, indem ich ein anderes Zahnrad nehme und einen bestimmten Teil seiner Implementierung ändere. (Zumindest glaube ich nicht, ich weiß nicht viel über Autos).
Polymorphismus : Nachdem Sie eine Schnittstelle definiert haben, sollte alles, was diese Schnittstelle verwendet, vom Standpunkt der verfügbaren Operationen nicht unterscheidbar sein, und Sie sollten nicht wissen müssen, welche Implementierung für die Verwendung einer Schnittstelle verwendet wird. Hier kommt es auf die Subtypisierung und das Liskov-Substitutionsprinzip an .
Kopplung : Ein Schlüsselaspekt von OOP ist, dass wir die Dinge eng mit denselben Operationen verknüpfen und die verschiedenen Formen, die sie haben können, ausbreiten. Daten werden mit Operationen an diesen Daten gebündelt. Dies bedeutet, dass es sehr einfach ist, eine neue Form von Daten hinzuzufügen (eine neue Implementierung), aber es sehr schwierig ist, einer Schnittstelle eine neue Operation hinzuzufügen (da Sie jede Klasse aktualisieren müssen, die die Schnittstelle implementiert). Dies steht im Gegensatz zu algebraischen Datentypen in funktionalen Sprachen, in denen es sehr einfach ist, eine neue Operation hinzuzufügen (Sie schreiben nur eine Funktion, die alle Fälle behandelt), aber es ist schwierig, eine neue Variante hinzuzufügen (da Sie eine neue hinzufügen müssen) Fall für alle Ihre Funktionen).
quelle
Dies hängt von der Bedeutung des Wortes "Bedürfnis" ab.
Wenn "brauchen" bedeutet erfordert, nein, wir benötigen es nicht.
Wenn "Bedürfnis" bedeutet "bietet starke Vorteile", dann würde ich sagen: "Ja", wir wünschen es.
Großes Bild
OO-Sprachen binden Funktionalität an Daten.
Sie können diese Bindungs- und Schreibfunktionen vermeiden, die Datenwerte weitergeben.
Aber dann werden Sie wahrscheinlich mit Datenkonstellationen enden, die zusammenpassen, und Sie werden beginnen, Tupel, Datensätze oder Datenwörterbücher weiterzugeben.
Und das sind wirklich alle Methodenaufrufe: Teilfunktionen für gebundene Datensätze.
Merkmal für Merkmal
Eigenschaften von OOP:
Keines dieser Dinge ist jedoch so einfach wie bei einer objektorientierten Sprache mit erstklassiger Unterstützung dieser Funktionen.
Verweise
Es gibt viele Kritiker von OOP .
Studien scheinen jedoch darauf hinzudeuten, dass wir durch die Wiederverwendung von Code durch OOP eine höhere Programmiererproduktivität erzielen. Dies ist eine kontroverse Feststellung, und einige Forscher geben an, dass sie diese Produktivitätsgewinne unter bestimmten Bedingungen nicht reproduzieren können. (Quelle)
Fazit
Wir brauchen keine OOP. Aber in einigen Fällen möchte der Benutzer OOP.
Mein Verständnis ist, dass ausgereifte Programmierer im objektorientierten Stil sehr produktiv sein können. Und wenn Pakete Kernobjekte mit einfachen Schnittstellen haben, die leicht zu verstehen sind, können selbst neue Programmierer sehr schnell produktiv werden.
quelle
Ich werde versuchen, mich kurz zu fassen.
Das Kernprinzip von OO ist die Kombination von Daten und Verhalten in einer einzigen Organisationseinheit (einem Objekt).
Dies ermöglicht es uns, die Komplexität zu kontrollieren, und es war ein ziemlich innovatives Konzept, als es herauskam. Vergleichen Sie das mit Dateien einerseits (reine Daten), Programmen, die diese Dateien andererseits (reine Logik) lesen und verarbeiten und ausgeben (wieder reine Daten).
Erst wenn Sie dieses Paket aus Daten und Logik zusammen haben und eine reale Entität modellieren, können Sie mit dem Austausch von Nachrichten beginnen, untergeordnete Klassen erstellen, private und öffentliche Daten und Verhaltensweisen trennen, polymorphes Verhalten implementieren und all diese OO-spezifischen magischen Aufgaben ausführen.
Also ja, OO ist eine große Sache. Und nein, es ist nicht nur ein Haufen alter Sachen mit einem ausgefallenen Namen.
Wenn man alles auseinander nimmt, die Elemente betrachtet und dann sagt "Oh, nun, hier gibt es nichts, was ich noch nie gesehen habe", erkennt man die Baugruppe nicht, in der sich die Innovation befindet. Das Ergebnis ist mehr als die Summe seiner Teile.
quelle
Es gibt keine "offizielle" Definition der objektorientierten Programmierung, und vernünftige Leute sind sich nicht einig darüber, was eigentlich die Qualität von OO definiert. Einige sagen Messaging, andere sagen Subtyping, andere sagen Vererbung, manche sagen die Bündelung von Daten und Verhalten. Das bedeutet nicht, dass der Begriff bedeutungslos ist, nur, dass Sie nicht zu sehr darüber streiten sollten, was echte OO ist.
Kapselung und Modularität sind grundlegendere Prinzipien des Designs und sollten in allen Programmierparadigmen angewendet werden. Befürworter von OO behaupten nicht, dass diese Eigenschaften nur mit OO erreicht werden können - nur, dass OO dafür besonders gut geeignet ist. Natürlich behaupten die Befürworter anderer Paradigmen wie etwa funktionale Programmierung dasselbe für ihr Paradigma. In der Praxis sind viele erfolgreiche Sprachen ein Multiparadigma, und das OO, das Funktionale usw. sollte eher als Werkzeug als als der "einzig wahre Weg" angesehen werden.
Stimmt, denn am Ende kann man in jeder Programmiersprache alles machen. In einigen Sprachen ist es möglicherweise einfacher als in anderen, da alle Sprachen unterschiedliche Stärken und Schwächen haben.
quelle
Etwas, das die anderen Antworten nicht erwähnt haben: Zustand.
Sie sprechen von OO als Werkzeug zum Verwalten von Komplexität . Was ist Komplexität? Das ist ein verschwommener Begriff. Wir alle haben dieses Gefühl dafür, was es bedeutet, aber es ist schwieriger, es festzuhalten. Wir könnten die zyklomatische Komplexität messen, dh die Anzahl der Laufzeitpfade durch den Code, aber ich weiß nicht, worüber wir sprechen, wenn wir OO zum Verwalten der Komplexität verwenden.
Ich denke, wir sprechen von staatlicher Komplexität.
Hinter der Verkapselung stehen zwei Hauptideen . Eine davon, das Verbergen von Implementierungsdetails , ist in den anderen Antworten ziemlich gut abgedeckt. Aber ein anderer versteckt seinen Laufzeitzustand . Wir spielen nicht mit den internen Daten von Objekten herum. Wir leiten Nachrichten weiter (oder rufen Methoden auf, wenn Sie Implementierungsdetails dem Konzept vorziehen, wie Jörg Mittag hervorhob). Warum?
Die Leute haben es bereits erwähnt, weil Sie die interne Struktur Ihrer Daten nicht ändern können, ohne den Code zu ändern, auf den zugegriffen wird, und Sie möchten dies an einer Stelle (der Zugriffsmethode) anstelle von 300 Stellen tun.
Das liegt aber auch daran, dass es schwierig ist, über den Code nachzudenken : Der prozedurale Code (ob in einer prozeduralen Sprache oder einfach so geschrieben) bietet wenig Hilfe, um die Mutation des Staates einzuschränken. Alles kann sich jederzeit und überall ändern. Das Aufrufen von Funktionen / Methoden kann aus der Ferne unheimlich sein. Automatisiertes Testen ist schwieriger, da der Erfolg der Tests durch den Wert nicht lokaler Variablen bestimmt wird, auf die häufig zugegriffen wird.
Die beiden anderen großen Programmierparadigmen (OO und funktional) bieten interessante, aber fast diametral entgegengesetzte Lösungen für das Problem der zustandsbezogenen Komplexität. Bei der funktionalen Programmierung wird versucht, dies gänzlich zu vermeiden: Funktionen sind im Allgemeinen rein, Operationen an Datenstrukturen geben Kopien zurück, anstatt das Original an Ort und Stelle zu aktualisieren, usw.
OO hingegen bietet Tools für den Umgang mit dem Verwaltungsstatus (anstelle von Tools zur Vermeidung). Zusätzlich zu den Tools auf Sprachebene wie Zugriffsmodifikatoren (geschützt / öffentlich / privat), Gettern und Setzern usw. gibt es auch eine Reihe verwandter Konventionen wie das Gesetz von Demeter, die davon abraten, durch Objekte zu greifen, um an andere Objekte Daten zu gelangen .
Beachten Sie, dass Sie nicht brauchen Objekte zu tun wirklich etwas davon: Sie einen Verschluss haben könnte , die unzugänglichen Daten hat und gibt eine Datenstruktur von Funktionen , die es zu manipulieren. Aber ist das nicht ein Objekt? Passt das nicht zu unserer Vorstellung davon, was ein Objekt ist, intuitiv? Und wenn wir dieses Konzept haben, ist es dann nicht besser, es in der Sprache zu wiederholen, als sich (wie andere Antworten sagten) auf eine kombinatorische Explosion konkurrierender Ad-hoc-Implementierungen zu verlassen?
quelle
Nein, aber sie können in vielen Situationen helfen.
Ich habe jahrzehntelang hauptsächlich eine einzige OO-Sprache verwendet, aber der größte Teil meines Codes ist tatsächlich streng prozedural vor OO. Für alles, was eine grafische Benutzeroberfläche betrifft, verwende ich die umfangreiche OO-Bibliothek der Sprache mit integrierten Methoden und Objekten, da dies meinen Code erheblich vereinfacht.
Beispielsweise erfordert eine Windows-Anwendung, die die ursprüngliche Windows-API auf niedriger Ebene zum Anzeigen eines Formulars, einer Schaltfläche und eines Bearbeitungsfelds verwendet, viel Code, während die Verwendung der Objektbibliotheken, die in Visual Basic oder C # oder Delphi enthalten sind, dasselbe bewirkt Programm winzig und trivial. Daher ist mein OO-Code in der Regel relativ klein und für die GUI gedacht, während mein Code, den diese Objekte aufrufen, in der Regel viel größer ist und sich normalerweise nicht darum kümmert, OO zu sein (obwohl er je nach dem zu lösenden Problem variieren kann).
Ich habe OO-Programme gesehen, die übermäßig kompliziert waren, sich auf komplizierte esoterische Regeln für die Implementierung der Objekte stützten und viel einfacher wären, wenn sie ohne OO-Konzepte geschrieben worden wären. Ich habe auch das Gegenteil gesehen: Komplexe Systeme, die darauf bedacht sind, mithilfe von Objekten neu implementiert und vereinfacht zu werden.
Wenn Sie Erfahrung sammeln, werden Sie feststellen, dass unterschiedliche Situationen unterschiedliche Werkzeuge und Lösungen erfordern und eine Größe nicht für alle geeignet ist.
quelle
Als jemand, der an einem sehr großen Projekt beteiligt ist, das vollständig in C geschrieben ist, kann ich definitiv sagen, dass die Antwort ein klares "Nein" ist.
Modularität ist wichtig. Modularität kann jedoch in praktisch jeder anständigen Sprache implementiert werden. Beispielsweise unterstützt C die modulare Kompilierung, Header-Dateien und Strukturtypen. Dies ist für 99% der Fälle ausreichend. Definieren Sie ein Modul für jeden neuen abstrakten Datentyp, den Sie benötigen, und definieren Sie die Funktionen für die Bearbeitung des Datentyps. Manchmal möchten Sie Leistung und diese Funktionen sind in der Header-Datei als Inline-Funktionen, ein anderes Mal verwenden Sie Standardfunktionen. Es ist alles für den Benutzer unsichtbar, welcher Weg gewählt wird.
Strukturen unterstützen die Zusammensetzung. Beispielsweise können Sie eine gesperrte Hash-Tabelle haben, die aus einer Mutex-Sperre und einer regulären Hash-Tabelle besteht. Dies ist keine objektorientierte Programmierung; Es wird keine Unterklasse erstellt. Komposition ist ein Werkzeug, das viel älter ist als die Idee der objektorientierten Programmierung.
Für die 1% der Fälle, in denen die Modularität der Kompilierungsebene nicht ausreicht und Sie Laufzeitmodularität benötigen, gibt es einen sogenannten Funktionszeiger. Sie ermöglichen individuelle Implementierungen einer genau definierten Schnittstelle. Beachten Sie, dass dies keine objektorientierte Programmierung in einer nicht objektorientierten Sprache ist. Dies definiert eine Schnittstelle und implementiert sie dann. Beispielsweise wird hier keine Unterklasse verwendet.
Betrachten Sie das vielleicht komplexeste Open Source-Projekt, das es gibt. Nämlich den Linux-Kernel. Es ist vollständig in der C-Sprache geschrieben. Dies geschieht hauptsächlich unter Verwendung der Standard-Modularitätstools auf Kompilierungsebene, einschließlich Komposition, und gelegentlich, wenn Laufzeitmodularität erforderlich ist, werden Funktionszeiger zum Definieren und Implementieren einer Schnittstelle verwendet.
Wenn Sie versuchen, ein Beispiel für objektorientierte Programmierung im Linux-Kernel zu finden, ist es sicher sehr schwierig, ein solches Beispiel zu finden, es sei denn, Sie erweitern die objektorientierte Programmierung um Standardaufgaben wie "Definieren einer Schnittstelle und anschließende Implementierung".
Beachten Sie, dass selbst die Programmiersprache C die objektorientierte Programmierung unterstützt, wenn Sie sie wirklich benötigen. Betrachten Sie beispielsweise das GTK-Toolkit für grafische Benutzeroberflächen. Es ist tatsächlich objektorientiert, obwohl es in einer nicht objektorientierten Sprache geschrieben ist. Dies zeigt also, dass die Vorstellung, dass Sie eine "objektorientierte Sprache" benötigen, zutiefst fehlerhaft ist. Es gibt nichts, was eine objektorientierte Sprache nicht kann. Wenn Sie ein erfahrener Programmierer sind, können Sie außerdem sehr einfach objektorientierten Code in einer beliebigen Sprache schreiben. Zum Beispiel ist die Verwendung von C keine Belastung.
Die Schlussfolgerungen sind also, dass objektorientierte Sprachen wahrscheinlich nur für unerfahrene Programmierer nützlich sind, die nicht verstehen, wie das Konzept tatsächlich implementiert wird. Ich möchte jedoch nicht in der Nähe eines Projekts sein, bei dem die Programmierer so unerfahrene Programmierer sind.
quelle
Der Grund für die Einführung von Programmierparadigmen, einschließlich objektorientierter Methoden, besteht darin, die Erstellung komplexerer und leistungsfähigerer Programme zu vereinfachen. In der August 1981-Ausgabe des Byte-Magazins definierte Daniel Ingalls , einer der Hauptentwickler von Smalltalk, "objektorientiert" mit den folgenden Funktionen:
Dies waren die Prinzipien, die Ingalls als Überlegungen zum Fahrdesign für SmallTalk-80 identifizierte, wie sie von Xerox Parc Research entwickelt wurden. In dem oben genannten Magazinartikel können Sie eine detaillierte Beschreibung der einzelnen Prinzipien lesen und erfahren, wie sie zu dem objektorientierten Paradigma nach Ingalls beitragen.
Alle diese Prinzipien können mit jeder Turing-vollständigen Sprache angewendet werden, sei es prozedural, Assembler oder was auch immer. Dies sind Gestaltungsprinzipien, keine Sprachspezifikation. Eine objektorientierte Sprache soll es einfacher machen, diese Prinzipien beim Erstellen von Software anzuwenden.
Um beispielsweise das erste von Ingalls Prinzipien (automatisches Speichermanagement) zu übernehmen, kann jeder sein eigenes automatisches Speichermanagementsystem in einer prozeduralen Sprache schreiben, aber es wäre eine Menge Arbeit, dies zu tun. Wenn Sie eine Sprache wie SmallTalk oder Java verwenden, in der eine automatische Speicherverwaltung integriert ist, muss der Programmierer nicht so viel Arbeit für die Speicherverwaltung leisten. Der Nachteil ist, dass der Programmierer weniger Kontrolle über die Art und Weise erhält, wie der Speicher verwendet wird. Es gibt also einen Vorteil und einen Nachteil. Die Idee eines Entwurfsparadigmas wie objektorientierte Programmierung ist, dass die Vorteile des Paradigmas die Nachteile für zumindest einige Programmierer überwiegen.
quelle
Eine Möglichkeit zum Verwalten der Softwarekomplexität besteht darin, das Framework mithilfe einer domänenspezifischen Sprache vollständig von den gewünschten Aktionen zu trennen . Dies bedeutet, dass sich die Ebene des Programmcodes von der Ebene unterscheidet, auf der die gewünschten Ergebnisse konfiguriert werden - eine völlig andere Sprache oder ein völlig anderes System. Wenn dies richtig gemacht wird, wird der herkömmliche Code im Wesentlichen zu einer Bibliothek, und der Benutzer oder eine andere Person, die die gewünschten Ergebnisse erstellt, verbindet die Dinge mit einer Skriptsprache oder einem visuellen Entwurfstool, wie beispielsweise einem Berichtsgenerator.
Um zu funktionieren, muss eine strikte Grenze gezogen werden, welche Operationen möglich sind und wie sie verknüpft sind (Skriptsprache oder visuelles Design, wie ein Formularerstellungstool). Metadaten sind eine wichtige Methode, um die Laufzeitkonfiguration von den Codierungsdetails abzugrenzen, sodass ein System eine Vielzahl gewünschter Ergebnisse unterstützen kann. Wenn die Grenzen festgelegt und eingehalten werden (und nicht jede Anfrage nach einer Erweiterung akzeptiert wird), können Sie ein langlebiges und robustes System haben, das für Menschen funktioniert, ohne dass sie Programmierer sein müssen, um das zu erreichen, was sie wollen.
Martin Fowler hat ein Buch darüber geschrieben, und die Technik ist fast so alt wie das Programmieren. Man könnte fast sagen, dass alle Programmiersprachen domänenspezifische Sprachen sind. Die Idee ist also endemisch und wird übersehen, weil sie so offensichtlich ist. Sie können jedoch auch eigene Skripte oder visuelle Design-Tools erstellen, um Ihr Leben zu vereinfachen. Manchmal ist es viel einfacher, ein Problem zu verallgemeinern!
quelle
Dies ist eine sehr gute Frage, und ich bin der Meinung, dass die hier gegebenen Antworten nicht gerechtfertigt sind. Deshalb werde ich fortfahren und meine Gedanken hinzufügen.
Das Ziel ist - Verwalten der Softwarekomplexität . Das Ziel ist nicht "OO-Sprache verwenden".
Das ist kein Grund, ein neues Paradigma einzuführen. Es ist etwas, das natürlich passiert ist, als die Codierung reifer wurde. Es ist sinnvoller, Code zu schreiben, in dem wir am Ende des Zuges einen Bus hinzufügen (der Zug wird anhand einer verknüpften Liste modelliert), als einen neuen Knoten am Ende der verknüpften Liste hinzuzufügen.
Codierung in Bezug auf den realen Welt Entitäten ist einfach die offensichtliche und korrekte Art und Weise zu codieren , wenn wir sind Codierung über die reale Welt Einheiten.
Ein Computer kann mit dem Hinzufügen eines Knotens am Ende der verknüpften Liste genauso einfach arbeiten wie mit dem Hinzufügen eines zusätzlichen Reisebusses am Ende des Zuges. Für Menschen ist es jedoch einfacher, mit dem Zug und dem Reisebus zu arbeiten als mit verknüpften Listen und Knoten, obwohl der Zug, wenn wir eine Ebene tiefer gehen, anhand einer verknüpften Liste modelliert wird.
Durch das Schützen oder Verschlüsseln der Dateien kann keine Kapselung erreicht werden. Das Gegenteil von Verschlüsselung ist Entschlüsselung. Das Gegenteil von Kapselung ist Dekapselung , dh Dekomposition von Strukturen und Klassen in Programmiersprachen, um eine bessere Leistung zu erzielen. Die Leistung, die durch die Reduzierung des Speicherverkehrs und die Vermeidung von OOP-Regelprüfungen erzielt wurde.
Daher können Sie Code schreiben, der sowohl verschlüsselt als auch gut gekapselt ist, da diese beiden Konzepte unterschiedlich sind.
Die Kapselung hilft bei der Verwaltung der Komplexität, indem sie der Realität nahe kommt.
Daher programmieren Sie Objekte ein, weil es für Sie einfacher ist, zu programmieren, und es für Sie und alle anderen schneller ist, dies zu verstehen.
quelle
Das Einzige, woran Sie sich erinnern sollten, ist
Folgendes : Bei OOP geht es nicht um Sprachfunktionen. Es geht darum, wie Sie Ihren Code strukturieren .
OOP ist eine Art zu denken und die Architektur Ihres Codes zu entwerfen. Sie kann in nahezu jeder Sprache durchgeführt werden. Dies schließt insbesondere die einfachen Nicht-OO-Sprachen ein, die als Assembler und C bezeichnet werden. Sie können in Assembler perfekt objektorientiert programmieren, und der in C geschriebene Linux-Kernel ist in vielerlei Hinsicht sehr objektorientiert .
Das sei gesagt, OO - Features in einer Sprache stark die Menge an Standardcode reduzieren , die Sie benötigen , schreiben die gewünschten Ergebnisse zu erzielen . Wenn Sie eine virtuelle Funktionstabelle explizit definieren und mit den entsprechenden Funktionszeigern in C füllen müssen, tun Sie in Java nichts und sind fertig. OO-Sprachen entfernen einfach alles, was cruft aktiviert, aus dem Quellcode und verbergen es hinter netten Abstraktionen auf Sprachebene (wie Klassen, Methoden, Member, Basisklassen, implizite Konstruktor- / Destruktor-Aufrufe usw.).
Also, nein, wir nicht brauchen OO - Sprachen OOP zu tun. Es ist nur so, dass OOP mit einer anständigen OO-Sprache so viel einfacher ist.
quelle
Objektorientierte Programmierung ist mehr als nur Module + Kapselung. Wie Sie sagen, ist es möglich, Module + Kapselung in einer nicht objektorientierten (prozeduralen) Sprache zu verwenden. OOP beinhaltet mehr als nur das: Es beinhaltet Objekte und Methoden. Also, nein, das erfasst OOP nicht. Siehe z. B. https://en.wikipedia.org/wiki/Object-oriented_programming oder die Einführung in OOP in einem guten Lehrbuch.
quelle
Der Hauptgrund dafür ist, dass ein Programm immer komplexer wird und Sie einige Teile davon für andere Teile unsichtbar machen müssen. Andernfalls lässt die Komplexität der App und die Anzahl der Funktionen Ihr Gehirn aus Ihren Ohren tröpfeln.
Stellen wir uns ein System von 100 Klassen mit jeweils etwa 20 Operationen vor, die mit ihnen ausgeführt werden können. Das sind 2.000 Funktionen. Davon sind jedoch möglicherweise nur 500 vollständige Vorgänge wie "Speichern" und "Löschen", während 1500 interne Funktionen sind, die ein wenig Wartung ausführen oder eine nützliche Funktion haben. Erwägen;
So
SetName
ist eine Funktion , die Menschen tun sollten , um eine Person, sondernSplitPersonName
ist eine Nutzenfunktion verwendet durch die Person.Eine direkte prozedurale Programmierung macht keinen Unterschied zwischen diesen beiden Operationen. Das bedeutet, dass Ihre 2.000 Funktionen um Ihre Aufmerksamkeit konkurrieren. Wenn wir diese Funktionen jedoch als "für jeden verfügbar, der über einen Personendatensatz verfügt" und "nur als Hilfsprogrammfunktion innerhalb des Personendatensatzes verwendet" kennzeichnen könnten, haben wir jetzt 500 "für alle" -Funktionen und 15 "Hilfsprogramm". Funktionen für die Klasse, die Sie bearbeiten.
Das ist was
public
undprivate
mach es;quelle