Haftungsausschluss
Der Artikel impliziert keine grundlegend neue Sichtweise der Dinge, außer unter dem Gesichtspunkt, dieses Material von "absoluter Null" zu studieren.

Das Material basiert auf Notizen von vor ungefähr 7 Jahren, als mein Weg zum Studium von OOP ohne IT-Ausbildung gerade erst begann. Damals war MATLAB die Hauptsprache, viel später wechselte ich zu C #.
Die Aussage über die Prinzipien von OOP, die ich anhand von Beispielen in Form einiger Äpfel, Birnen, die von der "Frucht" -Klasse geerbt wurden, und einer Reihe von Begriffen (Vererbung, Polymorphismus, Kapselung usw.) fand, wurde als chinesischer Buchstabe wahrgenommen.
Im Gegenteil, jetzt nehme ich aus irgendeinem Grund solches Material normal wahr, und die Präsentation aus meinem eigenen Artikel erscheint manchmal verwirrend und lang.
Aber meine alten Notizen und der überlebende schreckliche Code auf den Holodisken im Pipboy zeigen, dass die "klassische" Präsentation zu diesem Zeitpunkt ihre Funktionen nicht erfüllte und völlig erfolglos war. Vielleicht ist da etwas dran.
Wie sehr dies der Realität und Ihren eigenen Vorlieben entspricht - entscheiden Sie selbst ...
Voraussetzungen für OOP
Wandcode
Als ich gerade anfing, in MATLAB'e zu schreiben, war dies der einzige Weg zu schreiben und wusste wie. Ich wusste über Funktionen Bescheid und dass das Programm in Teile unterteilt werden kann.
Der Haken war, dass alle Beispiele saugten. Ich öffnete jemandes Kursbuch, sah dort kleine Körperfunktionen von 2-3 Zeilen, insgesamt funktionierte das NICHT (etwas fehlte) und es funktionierte nur, wenn ich diesen Müll wieder zu einer "Wand" zusammensetzte.
Dann schrieb ich mehrmals einige kleine Programme und fragte mich jedes Mal, warum es etwas zu teilen gab. Erst später kam das Verständnis: Der Code "wall" ist der Normalzustand eines Programms von ca. 1,5 A4-Seiten. Keine Funktionen und, Gott bewahre, OOP wird dort NICHT benötigt.
So sieht das Matlab-Skript aus (aus dem Internet).
Fs = 1000; % Sampling frequency
T = 1/Fs; % Sample time
L = 1000; % Length of signal
t = (0:L-1)*T; % Time vector
% Sum of a 50 Hz sinusoid and a 120 Hz sinusoid
%x = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t);
%y = x + 2*randn(size(t)); % Sinusoids plus noise
y=1+sin(100*pi*t);
plot(Fs*t(1:50),y(1:50))
title('Signal Corrupted with Zero-Mean Random Noise')
xlabel('time (milliseconds)')
figure
NFFT = 2^nextpow2(L); % Next power of 2 from length of y
Y = fft(y,NFFT)/L;
f = Fs/2*linspace(0,1,NFFT/2+1);
% Plot single-sided amplitude spectrum.
plot(f,2*abs(Y(1:NFFT/2+1)))
title('Single-Sided Amplitude Spectrum of y(t)')
xlabel('Frequency (Hz)')
ylabel('|Y(f)|')
Code in Funktionen unterteilen
Warum der Code immer noch in Teile geteilt wird, vermutete ich, als sein Volumen völlig unvorstellbar wurde (jetzt fand ich Scheißcode im Archiv - 650 Zeilen an einer Wand). Und dann erinnerte ich mich an die Funktionen. Ich wusste, dass Sie Ihren Code in kleine Blöcke aufteilen konnten, die einfacher zu debuggen und wiederzuverwenden sind.
Aber der Trick ist anders - aus irgendeinem Grund schweigen alle Lehrmaterialien darüber, wie viel eine Funktion von Variablen hat ...
Ein Mathematikkurs sagte, dass eine Funktion y = f (x)
ist. Dies wird als "Funktion einer Variablen" bezeichnet. Zum Beispiel ist y = x 2 ein ganzes PARABOL!
Mathematisches Problem: Konstruieren Sie ein PARABOL nach Punkten. In einem Notizbuchblatt, in einer Schachtel.
. z=f(x,y). — — . , .. . .

, « », . , , . – . .
-…

Und wenn die Funktion vier oder mehr Variablen hat…. Superstringtheorie. Calabi-Yau-Sorte. Sterblich. Nicht gegeben. Verstehe ...
Kurz gesagt, das ist alles falsch. Bei der Programmierung ist der normale Zustand einer Funktion doppelt vaginal doppelt anal . Es werden 100 Variablen benötigt und dieselben zurückgegeben, was in Ordnung ist. Eine andere Sache ist abnormal - sie mit einer COMMA aufzulisten.

Über die Tatsache, dass man irgendwie anders schreiben kann, wurde mir klar, als ich HIER DIESES Marine
function work = SelectFun(ProtName,length_line,num_length,angleN_1,angleN_2,num_angleN,angleF_1,angleF_2,num_angleF, res_max, num_res,varargin)
global angleF angleN model_initialized
Eine Reihe von Variablen, die durch eine COMMA getrennt sind. Und der aufrufende Code hat völlig andere Namen für diese Parameter, etwa SelectFun (a, b, c, d…). Daher müssen Sie sich merken, wo sich welche Variable befindet. Und treffen Sie ihre Vereinbarung durch die COMMA. Und wenn der Code modernisiert wird und sich die Anzahl der Variablen ändert, müssen sie mit einem COMMA neu angeordnet werden.
Und warum waren globale (schießen!) Variablen in diesem Elend?
Bingo! Um nicht bei jedem Code-Upgrade über eine COMMA Variablen anzuordnen.
Aber die COMMA folgte mir immer noch wie in einem Albtraum.

Und Varargin erschien. Dies bedeutet, dass ich mit einem COMMA viel mehr Argumente in den aufrufenden Code einfügen kann ...
Und dann habe ich über Arrays nachgedacht. Tutorial-Beispiele sprachen aufgeregt darüber, dass ein Array so sein kann:
=
[1 2 3
4 5 6
7 8 9]
Und Sie sehen, X (2,3) = 6 und X (3,3) = 9, und wir ... wir können die Matrixmultiplikation auf solchen Arrays organisieren! In der letzten Lektion haben wir PARABOLS und jetzt MATRIXES… durchgesehen.
Und keine einzige Zeile dieser verdammten Lehrbücher ist kurz und klar: Sie benötigen Arrays, um eine Funktion von 100 Variablen zu erstellen und nicht durch eine COMMA aus ihrer Auflistung zu fallen.

Im Allgemeinen kam mir die Idee, alles in einen großen zweidimensionalen Tisch zu packen. Zuerst lief alles gut:
angles =
[angleN, angleN_1, angleN_2, num_angleN
angleF, angleF_1, angleF_2, num_angleF]
function work= SelectFun(ProtName, length_line, num_length, angles , res_max, num_res, varargin)
Aber ich wollte mehr. Und es sah so aus:
data=
[angleN, angleN_1, angleN_2, num_angleN
angleF, angleF_1, angleF_2, num_angleF
length_line, num_length, 0, 0
res_max,num_res, 0,0]
function work= SelectFun(ProtName,data,varargin)
Und alles scheint in Ordnung zu sein, aber ... NULL! Sie erschienen, weil ich heterogene Daten über verschiedene Zeilen streuen wollte und die Datenmenge verschiedener Typen unterschiedlich war ... Und wie sollte die Funktion diese Nullen verarbeiten? Was passiert, wenn ich den Code aktualisieren möchte? Ich muss den Handler für diese fiesen Nullen innerhalb der Funktion neu schreiben! Schließlich können einige der Variablen tatsächlich gleich Null sein ...
Ich habe nie danach gefragt ...
Im Allgemeinen habe ich so etwas über STRUCTURES gelernt.
Strukturen
Hier war es notwendig, die Präsentation über Methoden der Datenverpackung zu beginnen. Arrays mit einer "Tabelle" entstanden anscheinend historisch zuerst, und sie schreiben auch darüber - am Anfang. In der Praxis finden Sie viele Programme, bei denen Arrays entweder eindimensional oder gar nicht sind.
Die Struktur ist eine "Datei-Ordner" -Packung von Daten, ungefähr auf der Festplatte eines Computers.
Laufwerk D: \
X (Variablenordner - "Objekt" oder "Struktur")
- a.txt (Variablendatei mit Daten - "Objektfeld", englisches Feld. Nummer 5
wird gespeichert) - b.txt (Nummer 10 wird gespeichert )
- .txt
Y (variabler Unterordner - "Objekt")
- d.txt (Nummer 2 wird gespeichert)
- e.txt
Um es klarer zu machen, schreiben wir auf, wie der Pfad zur Datei d.txt im Windows Explorer angezeigt wird
D: \ X \ Y \ d.txt
Danach öffnen wir die Datei und schreiben dort die Nummer "2".
Nun - wie es im Programmcode aussehen wird. Es ist nicht erforderlich, auf das "lokale Stammlaufwerk" zu verweisen, daher ist D: \ einfach nicht vorhanden, und wir haben auch keine Dateierweiterung. Im Übrigen wird bei der Programmierung normalerweise ein Punkt anstelle eines Schrägstrichs verwendet.
Es stellt sich so heraus:
X.Y.d=2
%
X.a=5
X.b=10
-
X.c=X.a+X.b %.. .=5+10=15
X.Y.e=X.c*X.Y.d %.. X.Y.e=15*2=30
In matlab können Strukturen ( struct ) direkt vor Ort erstellt werden, ohne die Kasse zu verlassen, d. H. Der obige Code ist ausführbar, kann in die Konsole eingegeben werden und alles funktioniert sofort. Die Struktur wird sofort angezeigt und alle "Variablendateien" und "Variablenunterordner" werden dort sofort hinzugefügt. Leider ist es unmöglich, dies über C # zu sagen, die Struktur ( Struktur ) wird dort von Hämorrhoiden festgelegt.
Die Struktur ist ein kühlerer Verwandter des ARRAY OF TABLES, bei dem anstelle von Indizes ein Datei-Ordner-System verwendet wird. Structure = "Variablenordner", der "Variablendateien" und andere "Variablenordner" enthält (dh Art von Unterordnern).
Alles ist bekannt, alles ist genau das gleiche wie auf einem Computer, Ordner, Dateien darin, nur in Dateien gibt es keine Bilder, sondern Zahlen (obwohl Bilder auch möglich sind).
Dies ist eine fortgeschrittenere Version des Speicherns von Daten für die Übergabe an eine FUNKTION im Vergleich zu der Idee, eine ARRAY-TABELLE zu erstellen, insbesondere zweidimensional, und, was mich stört, ein Tesserakt, dreidimensional und mehrdimensional.
Die ARRAY-TABELLE kann in zwei Fällen verwendet werden:In der Realität wird ARRAY TABLE normalerweise nur als eindimensionale Zeile homogener Daten verwendet. Alles andere in normalen Programmen erfolgt nach dem Schema "Dateiordner".
- Sie ist klein (warum ist sie dann? Was, Sie können keine durch Kommas getrennten Argumente an die Funktion übergeben?).
- Entweder können Sie eine Schleife erstellen und das Suchen / Füllen automatisieren (dies ist nicht immer möglich).
Warum beginnen Programmierlehrbücher dann mit Arrays und Tabellen? !!!
Kurz gesagt, nachdem ich die Strukturen für mich "entdeckt" hatte, entschied ich, dass ich eine Goldmine gefunden hatte und schrieb dringend alles neu. Der beschissene Code sah ungefähr so aus:
Data.anglesN=[angleN, angleN_1,angleN_2, num_angleN]; %
Data.anglesF=[angleF, angleF_1, angleF_2, num_angleF]; %
Data.length_line= length_line;
Data.num_length= num_length;
Data.res_max= res_max;
Data.num_res= num_res;
function work= SelectFun(ProtName,Data,varargin)
Ja, Sie können hier Perfektionismus betreiben und eine Reihe verschachtelter Objekte erstellen, aber darum geht es nicht. Die Hauptsache ist, dass die Variable jetzt innerhalb der Funktion nicht durch ihre Ordnungszahl (wo sie in der Argumentliste steht, getrennt durch eine COMMA) indiziert wird, sondern durch ihren Namen. Und es gibt keine dummen Nullen. Und der Funktionsaufruf hat jetzt eine akzeptable Form, es gibt nur 2 BEFEHLE, die Sie ruhig ausatmen können.
Klassen
Das Konzept der "Klasse" brachte eine Menge Terminologie auf mich: Kapselung, Vererbung, Polymorphismus, statische Methoden, Felder, Eigenschaften, gewöhnliche Methoden, Konstruktor ... # @% !!! ..
Aus Unerfahrenheit, nachdem ich die Strukturen herausgefunden hatte, entschied ich, dass es nicht nötig war, Entitäten zu komplizieren unnötig und dachte: "Klassen sind wie die gleichen Strukturen, nur komplizierter."
Bis zu einem gewissen Grad ist es so. Genauer gesagt ist dies genau das, was es ist. Eine Klasse ist, wenn Sie sehr genau hinschauen, eine STRUKTUR (ein ideologischer Nachkomme eines Arrays durch eine Tabelle), die beim Starten des Programms erstellt wird (im Allgemeinen scheint dies nicht nur beim Start der Fall zu sein). Wie bei jedem Nachkommen von ARRAY TABLE werden dort Daten gespeichert. Auf sie kann zugegriffen werden, während das Programm ausgeführt wird.
Daher war meine erste Klasse ungefähr so (ich schreibe ein Beispiel in C #, statische Felder werden normalerweise nicht in matlab implementiert, sondern nur über eine Hack-Kurve mit persistenten Variablen in einer statischen Funktion).
public class Math{
public static double pi;
public static double e;
public static double CircleLength(double R){ //.. « »
return 2*Math.pi*R; //
}
}
Der obige Fall ist sozusagen die "grundlegende" Fähigkeit einer Klasse - dumm ein Array (eine Struktur) mit Daten zu sein. Diese Daten werden zu Beginn des Programms hineingeworfen, und von dort können sie genauso extrahiert werden, wie wir sie aus der obigen Struktur herausgezogen haben. Hierfür wird das statische Schlüsselwort verwendet .
Die Struktur -> wird überall erstellt und speichert Daten, die jederzeit eingegeben werden.
Die Klasse -> ist die Struktur, die beim Programmstart erstellt wird. Alle mit dem Wort statisch gekennzeichneten Felder speichern einfach Daten wie in einer normalen Struktur. Statische Methoden sind einfach Funktionen, die von einer Klasse aufgerufen werden, genau wie von einem Ordner.
double L=Math.CircleLength(10); //L=62,8
Math.pi=4; //
Ich hatte einen Gag - wenn Felder Variablen und Methoden Funktionen sind, wie werden sie dann an einem Ort gespeichert? Nach meinem Verständnis ist eine Funktion (Methode) in einer Klasse eigentlich keine Funktion, sondern ein Zeiger auf eine Funktion. Jene. es ist ungefähr die gleiche "Variable" wie pi in Bezug auf die Arbeit damit.
Kurz gesagt, am Anfang habe ich die Klassen in einem solchen Band genau verstanden und einen weiteren Scheißcode geschrieben, in dem NUR statische Funktionen verwendet wurden. Ansonsten habe ich als Ordner mit Funktionen überhaupt keine Klassen verwendet.
Dieser Punkt wurde auch durch die Tatsache erleichtert, dass Klassen genau so in MATLAB ausgeführt werden - wie in einem so dummen Ordner, dessen Name mit @ beginnt (wie @ Math, ohne Leerzeichen), in dem die realen Dateien mit der Erweiterung .m Funktionen (Methoden) sind und es gibt Header-Datei mit der Erweiterung .m, was erklärt, dass die CircleLength-Funktion wirklich zur Klasse gehört und nicht nur eine .m-Datei mit einer Nicht-OOP-Funktion ist.
@ Math% OrdnerJa, es gibt eine bekanntere Möglichkeit für eine normale Person, eine Klasse in eine .m-Datei zu schreiben, aber zuerst wusste ich nichts davon. Statische Felder in matlab sind nur konstant und werden beim Programmstart einmal geschrieben. Wahrscheinlich, um sich vor dem "Schleppnetz" zu schützen, das beschließt, Math.pi = 4 zuzuweisen (IMHO, absolut nutzloses und dummes Thema, wird kein normaler Mensch ein großes Projekt in Matlab schreiben, und ein Programmierer wird ein kleines Projekt debuggen, und so ist es unwahrscheinlich Er ist ein Idiot).
- Math.m% Header-Datei
- CircleLength.m% Funktionsdatei
Aber zurück zum Thema. Neben statischen Methoden verfügt die Klasse auch über einen Konstruktor. Ein Konstruktor ist im Grunde nur eine Funktion wie y = f (x) oder sogar y = f (). Es darf keine Eingabeargumente geben, es müssen Ausgabeargumente vorhanden sein, und dies ist immer eine neue Struktur (Array).
Was der Konstruktor macht. Er macht nur Strukturen. Logischerweise sieht es so aus:
| C # -Code | Ungefähres Boolesches Äquivalent (Pseudocode)
|
|---|---|
|
|
|
|
Shit Code auf Matlab, der ähnliche Strukturen ohne Klassen erstellt (wo die Klasse vorhanden ist - siehe unten):
function Y=MyClass() % MyClass, Y=F()
Y.a=5
Y.b=10
end
… -
Y=MyClass()
Und am Ausgang haben wir die Struktur
Y (Ordnervariable)Daraus ergibt sich tatsächlich, dass die sogenannten Klassenfelder (nicht statisch, ohne den statischen Schlüsselcode ) lokale Variablen sind, die innerhalb der Konstruktorfunktion deklariert sind. Die Tatsache, dass sie nicht im Konstruktor, sondern außerhalb für eine Art Teufel geschrieben sind, ist SYNTAXISCHER ZUCKER.
- a (Dateivariable entspricht 5)
- b (Dateivariable entspricht 10)
SYNTAX SUGAR - solche Bullshit-Funktionen einer Programmiersprache, wenn der Code so aussieht, als ob sie ihn direkt beim Schreiben verschleiern möchten. Andererseits wird es (angeblich) kürzer und schneller geschrieben.
Nachdem ich diese "Entdeckung" gemacht hatte, war ich, der damals nur in Matlab schrieb, unglaublich überrascht.
In matlab können diese Strukturen, wie ich oben geschrieben habe, ohne Konstruktoren an Ort und Stelle erstellt werden, indem einfach Ya = 5 , Yb = 10 geschrieben wirdGenau wie Sie im Betriebssystem können Sie Dateien und Ordner erstellen, ohne die Registrierkasse zu verlassen.
Und hier - eine Art "Konstruktor" und alle Felder der Struktur (im Matlab werden sie als Eigenschaften bezeichnet - Eigenschaften, obwohl Eigenschaften streng genommen dunkler sind als Felder) müssen bürokratisch in die Header-Datei geschrieben werden. Wozu? Der einzige Vorteil, den ich dann in diesem System gesehen habe, ist, dass die Strukturfelder vordefiniert sind, und dies ist wie "Selbstdokumentation" - Sie können immer sehen, was dort sein sollte und was nicht dort sein sollte. Hier ist so etwas, was ich damals geschrieben habe:
classdef MyClass
properties %
a
b
end
methods %
function Y=MyClass() % .
% () Y a, b
Y.a=5;
Y.b=10;
end
end
methods (Static) %
function y=f(x) %
y=x^2; % , !11
end
end
end
Jene. Sie haben alles richtig verstanden: Die Methoden sind nur statisch, der xs-Konstruktor ist für was (es steht in der Dokumentation geschrieben - Oh, Klassen müssen einen Konstruktor haben - nun, hier ist ein Konstruktor für Sie), ich wusste dummerweise nicht alles andere und entschied, dass ich Zen und OOP gelernt hatte.
Trotzdem schien es mir eine coole Idee zu sein, Funktionen (statische Methoden) nach Klassenordnern zu sammeln. Es gab viele von ihnen, und ich setzte mich, um Scheißcode zu schreiben.
Bürokratie
Und lief auf so etwas. Es gibt eine Reihe von Funktionen einer niedrigeren Logikebene (sie sind statisch und in Klassenordner gepackt, jetzt werden die Namen der Klassen weggelassen):
Y1=f1(X1);
Y2=f2(X2);
Y3=f2(X3);
…
Y20=f20(X20);
In kleinen Projekten ist es unmöglich, eine solche Dominanz von Funktionen zu erreichen. Bildungsbeispiele enthalten im Allgemeinen 2-3 Funktionen - wie "Sehen Sie, wie wir ein PARABOL bauen können".
Und hier - eine verdammte Wolke von Funktionen, und jede, ihre Mutter, jede hat ein Ausgabeargument, und was soll man mit ihnen allen machen? Setzen Sie Funktionen einer höheren ("führenden") Logikstufe ein! Normalerweise gibt es viel weniger davon (herkömmlicherweise 5 statt 20). Jene. Herkömmlicherweise müssen Sie diese Y1, Y2, Y3… .Y20 irgendwie nehmen und sie in einige Z1, Z2… Z5 vergewaltigen. Damit Sie später ein Treffen der Partei machen können und dabei:
A1=g1(Z1);
A2=g2(Z2);
…
A5=g5(Z5);
% , . , !
Aber Z1… Z5 kommen nicht einfach von alleine. Um sie zu erstellen, benötigen Sie FUNCTIONS-WRAPPERS. Herkömmlicherweise funktionieren sie in etwa so ...
function Z1=Repack1(Y1,Y7, Y19)
Z1.a=Y1.a+Y7.b*Y.19.e^2;
Z1.b=Y7.c-Y19.e;
%.... - Y1, Y7, Y19
% Z1.
% Z2…Z5,
% 4 . !
end
Und dann könnte es eine andere "Management" -Ebene geben ...
Kurz gesagt, ich erkannte, dass ich in einer logistischen Hölle war. Normalerweise konnte ich keine Daten aus einer FIGURE CLOUD mit kleinen Funktionen y = f (x) extrahieren, ohne weitere bürokratische Funktionen für das Umpacken von FIGURE CLOUD zu schreiben. Wenn die Daten auf eine höhere Ebene übertragen werden, benötigen wir mehr RAPPER. Das endgültige Programm ist durch und durch mit Bürokratie überfüllt - es gibt mehr Repacker als "Business Code". Die Ordner-für-Funktionen-Klassen lösen dieses Problem nicht - sie sammeln nur die bürokratischen Idioten-Repacker in Haufen.
Und dann habe ich beschlossen, diesen beschissenen Code zu modernisieren, und es stellte sich heraus, dass dies unmöglich ist, ohne den gesamten bürokratischen Teil zu sägen!
Genau wie das Leben in Russland ...
Ich erkannte, dass ich etwas falsch gemacht hatte und verstand OOP besser. Und die Lösung - wenn man es so betrachtet, war es ideologisch an der Oberfläche.
OOP Idee
Warum eine Reihe von Funktionen wie y = f (x) ausführen , die VERSCHIEDENE Ausgabeargumente Y1… .Y20 erzeugen , wenn Sie EIN Argument machen können ? Art von:
Y_all=f1(Y_all, X1);
Y_all=f2(Y_all, X2);
….
Y_all=f20(Y_all, X20);
Dann werden absolut alle Ergebnisse der Funktionen in eine Struktur, in ein Array, nur in die verschiedenen Kompartimente verschoben. Alle. Dann kann Y_all direkt nach oben in die obere Ebene des "Managements" übertragen werden.
Y_all=DO_MOST_IMPORTANT_SHIT(Y_all, options_how_to_do_this_shit)
All-All-All-Funktionen-SEALERS-BUREAUERS gehen in den Arsch! Alle Daten werden in EINER Y_all- Basis gesammelt , alle Funktionen auf niedriger Ebene stellen die Früchte ihrer Arbeit in verschiedene Y_all- Fächer , das „Management“ huscht durch alle Y_all- Fächer und tut, was es tun soll. Nichts überflüssiges, der Code ist schnell geschrieben und funktioniert super ...
Dies ist genau die Idee von OOP und besteht aus. In Lehrbüchern schreiben sie pädagogische Beispiele über Äpfel und Birnen und zeigen dann ein Programm in 5 Zeilen. In den Beispielen für 5 Zeilen ist seitdem überhaupt kein OOP erforderlich Die Datenübertragung an die „Top-Management-Ebene“ erfolgt direkt und problemlos.
OOP wird benötigt, wenn ein großes Projekt das Problem der "Bürokratisierung" ist ....
Aber zurück zum Punkt. In echtem OOP gibt es SYNTAX SUGAR. Das obige Beispiel mit Y_all verwendet nur Strukturen, Funktionen f (,,,) werden als statisch betrachtet. OOP ist eine Zuckermenge, wenn der Code so aussieht:
Y_all.f1(X1); % Y_all=f1(Y_all, X1),
Y_all.f2(X2);
….
Y_all.f20(X20);
Y_all.DO_MOST_IMPORTANT_SHIT(options_how_to_do_this_shit);
Jene. Wir haben uns für eine schlammige Syntax entschieden, in der Sie Y_all nicht zweimal schreiben können , sondern nur einmal. Denn Wiederholung ist die Mutter des Stotterns.
Der Rest der Erklärung "wie OOP funktioniert" läuft darauf hinaus zu erklären, wie syntaktischer Zucker funktioniert.
Wie OOP Syntactic Sugar funktioniert
Zunächst muss diese Datenbank Y_all natürlich erstellt werden, bevor sie als Argument für die Funktion verwendet wird. Dies erfordert einen Konstruktor.
Zweitens ist es ratsam, vorzugsweise im Voraus vorherzusagen, welche "Fächer" es haben wird. Solange die Y_all- Datenbank klein ist, ist diese Einstellung ärgerlich. Ich möchte von "im laufenden Betrieb erstellten Klassen" träumen, ähnlich wie in MATLAB können Sie Strukturen mit einfachen Befehlen Ya = 5 , Yb = 10 erstellen . Der Wunsch, über dieses Thema zu phantasieren, verschwindet jedoch nach dem Debuggen eines gesunden Projekts.
Weiter - Aufruf der Methode (Funktion).
So hat es sich ungefähr entwickelt
| Funktion | Kommentar |
|---|---|
| Y = f (X) | Dies war in der Mathematik der Fall, als wir ein PARABOL nach Punkten zeichneten! |
| X = f (X) | Wir wurden von Bürokraten gemobbt, und wir haben für alle Gelegenheiten |
| f (X) | Warum sollte eine Funktion ein Argument zurückgeben? Dies ist der Archaismus der Zeiten des Mathematikunterrichts! Und eine sinnlose Verschwendung von Erinnerung! Lassen Sie die Daten als Referenz übergeben werden, dann kommt die Funktion selbst zum Argument, ändert und verlässt. NICHTS = f (X)
Nicht der Berg geht zu Mohammed, sondern Mohammed zum Berg. |
| X.f () | Wir haben gerade das X-Argument mit syntaktischem Zucker herausgezogen. NICHTS = X.f (NICHTS) |
Nun - wie ist eine solche Funktion intern angeordnet, die NICHTS nimmt und NICHTS zurückgibt (das Schlüsselwort void in C #).
Mir gefällt, wie dies in matlab gemacht wird (vom Standpunkt des Verstehens aus gesehen): Die Funktion, die wir als Xf () aufrufen, wird intern als geschrieben
| Beispiel für einen MATLAB-Code | Beispiel-C # -Code |
|---|---|
|
|
| « » . — ( , this, fuck, shit).
this, . |
« » . , ( )!
! , « this». «» this ( ). |
Hier ist eine Funktion mit "das Standardargument ist dies ", die in der Klasse liegt, wie in einem Ordner - es gibt eine gewöhnliche Methode (xs, wie es auf Russisch korrekt ist).
In der Tat, alle Argumente in einer einzigen pauken dieses ist nicht immer richtig. Manchmal benötigen Sie andere Argumente (z. B. Benutzereingaben):
public void f(int user_input) {
this.c=this.a+this.b + user_input;
}
Manchmal müssen Sie sogar ein Argument zurückgeben (z. B. über den Erfolg oder Misserfolg einer Operation) und nicht void schreiben . Dies ändert jedoch nichts an der Statistik: Die meisten OOP-Funktionen geben NICHTS ( void ) zurück und akzeptieren entweder nichts (das Standardargument zählt nicht) oder nur sehr wenige Argumente.
Schreiben wir den endgültigen Code
in MATLAB
classdef MyClass<handle % handle
properties %
a
b
end
methods %
function this=MyClass(a, b) % . a, b -
this.a=a
this.b=b
end
function f(this)
this.c=this.a+this.b
end
end
end
% - Untitled.m
X=MyClass(5,10);
X.f();
fprintf(‘X.c=%d',X.c) % .=15
Jetzt in C #:
public class MyClass {
public int a;
public int b;
public MyClass(int a, int b) { // . a, b - ()
this.a=a;
this.b=b;
}
public void f(this) {
this.c=this.a+this.b
}
}
// -
MyClass X=new MyClass(5,10);
X.f();
Console.WriteLine(“X.c={0}”,X.c); // .=15
Als ich es herausfand, schienen die meisten Probleme beim Schreiben von Code in den Hintergrund getreten zu sein ...
Eigenschaften vs Felder
Schauen wir uns ein Beispiel an.
| ohne Eigenschaften | mit Eigenschaften |
|---|---|
|
|
|
|
| Kommentar: Das Argument
Set_a kann wie auch immer Set_a (int YourVarName) aufgerufen werden. |
Kommentar: Eine Variable in
set {...} sollte immer als Wert bezeichnet werden |
Dieses Ding ist sehr praktisch und wird oft benutzt, aber es ist immer noch SYNTAX SUGAR.
Feld ist eine vollständig qualifizierte Variable. Die Eigenschaft besteht aus 2 Klassenmethoden (get und set), deren Aufrufsyntax "Variablenaufruf" kopiert.
In der Tat können Sie in get and set Mist machen:
int A {
get{ return 0;}
set{ Console.WriteLine(""); }
}
Daher wird empfohlen, die Namenseigenschaften mit einem Großbuchstaben und Felder mit einem kleinen Buchstaben zu schreiben.
Es kommt vor (Sie können beispielsweise kein Feld in Schnittstellen erstellen), dass Sie Eigenschaften schnell ausführen müssen. Dann können Sie:
int A { get; set;} // , - _a
// set get .
public int B { get; private set;} //
//( , )
Vererbung, Einkapselung, Polymorphismus
Warum hast du sie noch nicht erwähnt? DennWenn es einen Prozess gibt, bei dem Sie die Schreibfähigkeiten im OOP-Stil beherrschen
- tatsächlich sind sie beim Schreiben von Code nicht so gefragt, wie sie in der Abfrage "Ok Google, was ist OOP" erwähnt werden. Ich würde sogar sagen, dass sie anfangs praktisch unnötig sind .
- Wo sie gebraucht werden, können Sie darüber lesen (nur die Faulen haben nicht über diesen Fall geschrieben).
- Sie haben die meisten Klassen OHNE Vererbung. Sie schreiben einfach ALLE Funktionen in die erforderliche Klasse und müssen nicht wirklich etwas erben.Dann wachsen Ihre Hände bis zu Ihren Schultern und Sie werden es selbst herausfinden, ohne diesen Artikel , wo Sie dies NICHT tun sollten, insbesondere wo Sie NICHT öffentlich schreiben sollten.
- Dementsprechend geht der Polymorphismus (eine Lotion zur Vererbung) auch durch den Wald
- "Kapselung" reduziert sich darauf, überall öffentlich zuzuweisen (allen Feldern, Eigenschaften und Methoden).
Aber noch ein
Erbe. Dies ist ein cleveres Copy-Paste
Eine fehlerhafte Implementierung von "Vererbung" sieht folgendermaßen aus:
Oh, mein Shit-Code hat eine Klasse namens MyClass, und es fehlen ein weiteres SHIT-Feld und eine weitere DO_THE_SHIT () -Methode!Trotzdem sind wir zivilisiertere Menschen, und wir wissen, dass es besser ist, den Text des Programms nicht zu kopieren, sondern darauf zu verweisen.
* Strg + C, Strg + V
* Eine neue Klasse MyClass_s_fichami wird erstellt und die gewünschte wird dort hinzugefügt
Nehmen wir an, wir schreiben immer noch in einer alten Programmiersprache oder kennen so etwas wie "Vererbung" nicht. Dann schreiben wir 2 verschiedene Klassen
|
|
|
|
Was wir rechts getan haben, ist Vererbung. Dies geschieht nur in normalen Programmiersprachen mit einem Befehl:
public class MyClassB : MyClassA {
// MyClassA ,
// base
// a (, , property a) ,
// (. )
public int b;
public void F2(int x){ //
this.b=this.a*this.b;
}
public MyClassB(int a, int b){ //
// base A
//
this.a=a;
this.b=b;
}
}
Der Code funktioniert "außerhalb" genauso wie in Option 2. Dh. Das Objekt wird sozusagen zu einer "Matroschka" - innerhalb eines Objekts sitzt ein anderes Objekt dumm, und es gibt "Kommunikationskanäle", über die Sie direkt auf das interne Objekt verweisen können.
Spoiler Header
In Matlab ist die Situation etwas interessanter. Wenn Sie den untergeordneten Konstruktor MyClassB ausführen , wird der MyClassA- Vorfahrenkonstruktor nicht stillschweigend aufgerufen .
Sie müssen es direkt erstellen. Einerseits ist das ärgerlich:
classdef MyClassB<MyClassA
% ...
function MyClassB(a, b)
this@MyClassA(a); % , «»
this.b=b;
end
end
Wenn der Nachkomme jedoch überhaupt mit anderen Argumenten wie MyClassB (d) aufgerufen wird , können Sie eine Konvertierung im Inneren durchführen, etwa:
classdef MyClassB<MyClassA
% ...
function MyClassB(d)
a=d-5;
this@MyClassA(a);
this.b=d+10;
end
end
In C # kann dies nicht direkt durchgeführt werden, und dies führt dazu, dass eine Art "Konvertierungsfunktionen" geschrieben werden müssen:
class MyClassB:MyClassA{
//...
static int TransformArgs( int d) {return d-5;}
MyClassB(int d):base(TransformArgs(d)) {this.b=d+10;}
}
oder machen Sie "statische Konstruktoren" wie folgt:
class MyClassB:MyClassA {
//...
MyClassB(){} //
static MyClassB GetMyClassB(int d) {
var X=new MyClassB(); //
//
.a=d-5;
.b=d+10;
return X;
}
}
Es scheint um Vererbung, im Grunde alles.
Natürlich zwingt niemand den Erben, die " F1 " -Methode und die " a " -Eigenschaft so zu schreiben , dass sie notwendigerweise in den Aufruf der Methode und der Felder des Vorfahren übersetzt werden. Broadcasting ist nur das Standardverhalten der "Vererbung". Sie können (natürlich! Dies sind andere Methoden in einer anderen Klasse, Bruder) so schreiben:
public class MyClassB : MyClassA {
public int a{ //
get { return 0; }
set { base.a=0; }// this.fieldA.a=0;
}
public int b;
public void F1(int x){ // «»
// - base -
Console.WriteLine(“”);//
}
}
Verkapselung
... Konzeptionell bedeutet dies, dass sich innerhalb eines Objekts der Klasse MyClassB ein Objekt der Klasse MyClassA im Basisfeld befindet und Steuerbefehle außerhalb übertragen können. All dies ist oben geschrieben und macht keinen Sinn, es zu wiederholen.
Es gibt ein solches Thema mit verschiedenen Zugriffsmodifikatoren - öffentlich , privat , geschützt ... Über sie, was am interessantesten ist, es wird überall mehr oder weniger normal geschrieben, ich empfehle nur darüber zu lesen.
public - Dies bedeutet, dass das Feld , die Eigenschaft oder die Methode von außen sichtbar ist und gezogen werden kann.Wenn Sie NICHT wissen, was zu tun ist, schreiben Sie öffentlich (schlechter Rat, ja).
Finden Sie dann die Kraft in sich selbst und werfen Sie diese öffentliche (oder ersetzen Sie sie aus Gründen der Klarheit durch private ), wo immer dies nicht erforderlich ist (führen Sie ein "Refactoring" durch). Ja, natürlich ist es sehr gut, ein Visionär
privat - Dies bedeutet, dass das Feld , die Eigenschaft oder die Methode eines Dateiordnerobjekts nur innerhalb der Methoden dieser Klasse sichtbar ist.ABER ... Es ist eine Klasse, keine INSTANZ (Objekt). Wenn Sie einen Code haben wie:
class MyClassA{
private int a=10;
public void DO_SOMETHING(MyClassA other_obj) {
// DO_SOMETHING
// private MyClassA.
this.a=100; //
other_obj.a=100; //
}
}
var X=new MyClassA();
var Y=new MyClassA();
X.DO_SOMETHING(Y); // X.a=100, Y.a=100
So etwas wird beim Klonen verwendet (siehe andere Quellen für weitere Details).
Ich habe versucht, beim Schreiben von Code über diese Anordnung von öffentlich und privat nachzudenken . Dies ist ein inakzeptabel zeitaufwändiger Code beim Schruppen des Codes. Und dann stellt sich heraus, dass der Code selbst grundlegend anders gemacht werden muss.
Wenn der Code in Solo geschrieben ist, macht es keinen Sinn, sich vorher mit privat und öffentlich zu beschäftigen. Es gibt wichtigere Aufgaben, zum Beispiel, den Code tatsächlich zu entwickeln und zu schreiben ...
Der einzige Ort, an dem mehr oder weniger klar ist, an welcher Stelle privat und öffentlich zu setzen ist, ist der gleiche berüchtigte Eigenschaften, die sich auf eine Art Feld beziehen.
class MyClassA{
// private
private int a; //"private" C# .
// public
public int A {get{...;} set{...;}} // ""
}
An anderen Orten müssen Sie, um öffentlich und privat zu arrangieren, wirklich schauen, was das Programm tut, und höchstwahrscheinlich wird es nicht funktionieren, dies "in Abwesenheit" zu lernen.
geschützt - dies bedeutet " öffentlich " für alle Methoden der abgeleiteten Klassen und " privat " für alles andere.Im Allgemeinen ist es logisch, wenn wir annehmen, dass geerbte Klassen einfach als "komplexere Versionen" ihrer Vorfahren erscheinen.
Ehrlich gesagt habe ich schon vergessen, wo ich diesen Schutz ausdrücklich angewendet habe. Normalerweise entweder öffentlich oder privat. Die meisten Klassen, die ich schrieb, erbten nicht von anderen benutzerdefinierten Klassen, und wo sie es taten, gab es selten einen ernsthaften Bedarf für solche Dinge.
Der Eindruck ist, dass bei der Arbeit an einem großen Projekt nicht öffentliche Modifikatoren gefragt sind, die möglicherweise von einer Reihe von Personen unterstützt werden. Das Verständnis, wo sie verwendet werden sollen, wird erst nach einer langen Zeit des Einhaltens in einen kilometerlangen Code angezeigt. Wenn man „auf dem Schriftweg“ studiert, ist es schwierig, dieses Verständnis irgendwie zu vermitteln.
Polymorphismus
Als ich in Matlab schrieb, konnte ich nicht verstehen, warum Polymorphismus überhaupt benötigt wird und WAS ES IST.
Als ich dann zu C # wechselte, stellte ich fest, dass dies ein Merkmal von STRICTLY TYPICAL LANGUAGES ist und eine sehr schwache Beziehung zu OOP hat. In matlab können Sie überall schreiben, ohne über die Existenz dieses Polymorphismus Bescheid zu wissen - es gibt keine strikte Typisierung.
Der Einfachheit halber heißen die Klassen A und B.
class A{...}
class B:A{...}
A X=new B();
// x A, - B.
// .
B x_asB=new B();
A x_asA=(A) x_asB;
Dies wird als Typumwandlung bezeichnet. In C # können Sie SELBST (wenn Sie wissen wie) Ihre eigenen benutzerdefinierten Gusssysteme schreiben, von fast jedem Typ zu jedem anderen.
Hier - einfach "Casting" aus der Box. Da sich ein anderes Objekt der Klasse A in einem Objekt x befindet, das zur Klasse B gehört , besteht eine der scheinbar offensichtlichen Möglichkeiten des Castings darin, alle Verbindungen von einem externen zu einem internen Objekt zu schließen. Dies ist nicht wirklich notwendig, aber diejenigen, die den "Polymorphismus" erfunden haben, entschieden, dass dies am offensichtlichsten ist. Und der Benutzer wird den Rest der Optionen selbst schreiben. Entschuldigung für die (nicht mehr ganz relevante) "politota" der Stichprobe 2008-2012.
lass {...}
class : {...}
= new Me (); //
= () ; //
Schnittstelle
Wir müssen damit beginnen, wie man DIESES anwendet.
Nehmen wir an, wir haben eine Liste und möchten etwas hinzufügen.
In Matlab ist dies am einfachsten (als Zellenarray bezeichnet):
myList={1, ‘2’, ‘fuck’, ‘shit’, MyClassA(), MyClassB(), …. ,_, _};
Sie denken nicht, was für ein Objekt es ist, Sie nehmen es einfach und setzen es auf eine Liste.
Nehmen wir als nächstes an, Sie müssen die Liste durchlaufen und mit jedem Element etwas tun:
for i=1:length(myList)
item=myList(i);
% - item-
DoSomeStuff(item);
end
Wenn die DoSomeStuff-Funktion intelligent genug ist, um alles zu verdauen, was ihr zugeführt wird, wird dieser Code erfüllt.
Wenn die DoSomeStuff-Funktion (oder ihr Autor) nicht mit Intelligenz glänzt, besteht die Möglichkeit, an etwas zu ersticken: einer Zahl, einer Zeile, Ihrer selbstgemachten Klasse, dem kahlen Teufel oder - Gott bewahre - Ihrer Großmutter.
MATLAB zeigt rotes Fluchen in Englisch in der Konsole an und beendet Ihr Programm. Somit erhält Ihr Code automatisch einen Darwin Award.
Dies ist jedoch tatsächlich schlecht, da der Code manchmal sehr komplex ist. Dann werden Sie fest davon überzeugt sein, dass Sie alles richtig gemacht haben, aber tatsächlich wurde die fehlerhafte Kombination von Aktionen während des Testens einfach nie gestartet.
Aus diesem Grund (obwohl nicht nur, weil) auf MATLAB - ich habe es selbst geschafft (ungefähr wie bei KPDV), bei der schrecklichen Größe des Codes - besteht keine Notwendigkeit, große Projekte zu schreiben.
Fahren wir nun mit C # fort. Wir erstellen eine Liste und ... und werden gebeten, sofort den TYP des Objekts anzugeben. Wir erstellen eine Liste vom Typ Liste.
In eine solche Liste können Sie die Nummer 1 setzen.
In eine solche Liste können Sie die Nummer 2 setzen und sogar, Gott vergib mir, 3.
List<int> lst1=new List<int>().
lst.Add(1);
lst.Add(2);
lst.Add(3);
Textzeichenfolgen sind jedoch nicht mehr vorhanden. Objekte Ihrer selbstgemachten Klasse - streng genommen nicht. Ich schweige über den kahlen Teufel und deine Großmutter, sie können unter keiner Variante da sein.
Sie können eine separate Liste von Zeilen erstellen. Sie können - für Ihre selbst gemachten Klassen.
List<MyClassA> lst2=new List<MyClassA>();
lst2.Add(new MyClassA());
Tatsächlich können Sie - separat - Listen von Bald Devils, Ihren Großmüttern, erstellen.
Das Hinzufügen zu einer Liste funktioniert jedoch nicht. Ihr Code erhält einen Darwin-Preis, kombiniert mit Compiler-Missbrauch, bevor Sie überhaupt versuchen, ihn auszuführen. Der Compiler erlaubt Ihnen mit Bedacht nicht, die DoSomeStuff (item) -Funktion auszuführen , die mit ihrem Argument " erstickt ".
Dies ist sehr praktisch bei großen Projekten.
Aber was tun, wenn Sie es noch in eine kleine Liste aufnehmen möchten?
Das ist eigentlich kein Problem. Es ist genug , um alles zu Typ konvertieren Objekt . Fast (oder sogar absolut) alles kann in Typobjekte konvertiert werden .
List<object> lst=new List<object>();
lst.Add((object) new MyClassA());
lst.Add((object) new MyClassB());
Das Problem beginnt, wenn wir die Liste durchlaufen. Der Punkt ist, dass der Objekttyp (fast) nichts tun kann. Es kann nur vom Typ Objekt sein .
- Was kannst du tun?
- Ich kann singen und tanzen
- Und ich - Sancho ...
- Was kannst du tun, Sancho?
- Ich bin Sancho.
- Kannst du etwas tun?
- Sie verstehen nicht. Ich kann Sancho sein.
Daher ist die Schnittstelle geschrieben. Dies ist die Klasse, von der geerbt werden soll. Die Schnittstelle enthält Methoden- und Eigenschaftskopfzeilen.
In unserem Fall sind dies die Methoden und Eigenschaften, die den NORMAL-Betrieb der DoSomeStuff (item) -Funktion sicherstellen . Die Schnittstelle implementiert die Eigenschaften selbst nicht. Dies geschieht absichtlich. Tatsächlich könnte man einfach von einer Klasse erben, die von der DoSomeStuff () -Funktion verwendet werden kann . Das bedeutet aber zusätzlichen Code und einen vergesslichen Programmierer.
Wenn ein anderer Programmierer eine Schnittstelle geerbt hat, aber vergessen hat, die erforderlichen Eigenschaften und Methoden einer Klasse zu implementieren, schreibt der Compiler diese mit einem Darwin-Preis in den Code. So können Sie Folgendes tun:
interface ICanDoTheStuff {...};
class MyClassA: ICanDoTheStuff {…}
class MyClassB: ICanDoTheStuff {…}
static void DoSomeStuff(ICanDoTheStuff item) {…}
List<ICanDoTheStuff> lst= new List<ICanDoTheStuff>();
lst.Add(new MyClassA());
lst.Add(new MyClassB());
for (int i=0; i<lst.Count; i++) {
ICanDoTheStuff item=myList[i];
DoSomeStuff(item);
}
Jene. für die am Ende eine Schnittstelle benötigt wird - um eine typisierte Liste oder ein Feld in der Klasse zu erstellen und das Verbot zu umgehen, dort (zur Liste oder zum Feld) verbleibenden Müll hinzuzufügen.
Die Schnittstelle ist "Bürokratie". Nicht überall und nicht überall dort, wo es gebraucht wird, obwohl es in großen Projekten notwendig und nützlich ist.
... im Allgemeinen so etwas ... Ich entschuldige mich für die harten Ausdrücke, aus irgendeinem Grund scheint mir eine "trockene" Präsentation des Materials erfolglos zu sein ...