MATLAB Language
Objekt orientierte Programmierung
Suche…
Klasse definieren
Eine Klasse kann mit classdef
in einer .m
Datei mit demselben Namen wie die Klasse definiert werden. Die Datei kann den classdef
... end
Block und lokale Funktionen zur Verwendung in Klassenmethoden enthalten.
Die allgemeinste Definition der MATLAB-Klasse hat folgende Struktur:
classdef (ClassAttribute = expression, ...) ClassName < ParentClass1 & ParentClass2 & ...
properties (PropertyAttributes)
PropertyName
end
methods (MethodAttributes)
function obj = methodName(obj,arg2,...)
...
end
end
events (EventAttributes)
EventName
end
enumeration
EnumName
end
end
MATLAB-Dokumentation: Klassenattribute , Eigenschaftsattribute , Methodenattribute , Ereignisattribute , Aufzählungsklasseneinschränkungen .
Beispielklasse:
Eine Klasse namens Car
kann in der Datei Car.m
als definiert werden
classdef Car < handle % handle class so properties persist
properties
make
model
mileage = 0;
end
methods
function obj = Car(make, model)
obj.make = make;
obj.model = model;
end
function drive(obj, milesDriven)
obj.mileage = obj.mileage + milesDriven;
end
end
end
Beachten Sie, dass der Konstruktor eine Methode mit demselben Namen wie die Klasse ist. <Ein Konstruktor ist eine spezielle Methode einer Klasse oder Struktur in der objektorientierten Programmierung, die ein Objekt dieses Typs initialisiert. Ein Konstruktor ist eine Instanzmethode, die normalerweise denselben Namen wie die Klasse hat und zum Festlegen der Werte der Member eines Objekts verwendet werden kann, entweder auf Standardwerte oder auf benutzerdefinierte Werte.>
Eine Instanz dieser Klasse kann durch Aufrufen des Konstruktors erstellt werden.
>> myCar = Car('Ford', 'Mustang'); //creating an instance of car class
Das Aufrufen der drive
erhöht den Kilometerstand
>> myCar.mileage
ans =
0
>> myCar.drive(450);
>> myCar.mileage
ans =
450
Wert vs. Handle-Klassen
Klassen in MATLAB sind in zwei Hauptkategorien unterteilt: Wertklassen und Handhabungsklassen. Der Hauptunterschied besteht darin, dass beim Kopieren einer Instanz einer Wertklasse die zugrunde liegenden Daten in die neue Instanz kopiert werden, während die neue Instanz bei Handle-Klassen auf die ursprünglichen Daten verweist und durch das Ändern von Werten in der neuen Instanz im Original geändert wird. Eine Klasse kann als Handle definiert werden, indem Sie von der handle
Klasse erben.
classdef valueClass
properties
data
end
end
und
classdef handleClass < handle
properties
data
end
end
dann
>> v1 = valueClass;
>> v1.data = 5;
>> v2 = v1;
>> v2.data = 7;
>> v1.data
ans =
5
>> h1 = handleClass;
>> h1.data = 5;
>> h2 = h1;
>> h2.data = 7;
>> h1.data
ans =
7
Vererbung von Klassen und abstrakten Klassen
Haftungsausschluss: Die hier vorgestellten Beispiele dienen nur der Veranschaulichung der Verwendung von abstrakten Klassen und Vererbung und sind nicht unbedingt von praktischem Nutzen. Auch gibt es in MATLAB keine so polymorphe Sache und daher ist die Verwendung von abstrakten Klassen begrenzt. In diesem Beispiel soll gezeigt werden, wer eine Klasse erstellt, von einer anderen Klasse erbt und eine abstrakte Klasse zum Definieren einer allgemeinen Schnittstelle verwendet.
Die Verwendung von abstrakten Klassen ist in MATLAB eher begrenzt, kann jedoch bei einigen Gelegenheiten nützlich sein.
Nehmen wir an, wir wollen einen Meldungslogger. Wir können eine Klasse ähnlich der folgenden erstellen:
classdef ScreenLogger
properties(Access=protected)
scrh;
end
methods
function obj = ScreenLogger(screenhandler)
obj.scrh = screenhandler;
end
function LogMessage(obj, varargin)
if ~isempty(varargin)
varargin{1} = num2str(varargin{1});
fprintf(obj.scrh, '%s\n', sprintf(varargin{:}));
end
end
end
end
Eigenschaften und Methoden
Kurz gesagt: Eigenschaften enthalten den Status eines Objekts, während Methoden wie Schnittstellen aussehen und Aktionen für Objekte definieren.
Die Eigenschaft scrh
ist geschützt. Deshalb muss es in einem Konstruktor initialisiert werden. Es gibt andere Methoden (Getter), um auf diese Eigenschaft zuzugreifen. Dieses Beispiel ist jedoch nicht zulässig. Auf Eigenschaften und Methoden kann über eine Variable zugegriffen werden, die eine Referenz auf ein Objekt enthält, indem Punktnotation gefolgt von einem Namen einer Methode oder einer Eigenschaft verwendet wird:
mylogger = ScreenLogger(1); % OK
mylogger.LogMessage('My %s %d message', 'very', 1); % OK
mylogger.scrh = 2; % ERROR!!! Access denied
Eigenschaften und Methoden können öffentlich, privat oder geschützt sein. Geschützt bedeutet in diesem Fall, dass ich von einer geerbten Klasse aus auf scrh
zugreifen kann, aber nicht von außen. Standardmäßig sind alle Eigenschaften und Methoden öffentlich. LogMessage()
kann daher außerhalb der Klassendefinition frei verwendet werden. LogMessage
definiert auch eine Schnittstelle. Dies bedeutet, dass wir sie aufrufen müssen, wenn ein Objekt unsere benutzerdefinierten Nachrichten protokollieren soll.
Anwendung
Angenommen, ich habe ein Skript, in dem ich meinen Logger verwende:
clc;
% ... a code
logger = ScreenLogger(1);
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
Wenn ich mehrere Stellen habe, an denen ich denselben Logger verwende und ihn dann etwas komplexer gestalten möchte, beispielsweise eine Nachricht in eine Datei schreiben, muss ich ein anderes Objekt erstellen:
classdef DeepLogger
properties(SetAccess=protected)
FileName
end
methods
function obj = DeepLogger(filename)
obj.FileName = filename;
end
function LogMessage(obj, varargin)
if ~isempty(varargin)
varargin{1} = num2str(varargin{1});
fid = fopen(obj.fullfname, 'a+t');
fprintf(fid, '%s\n', sprintf(varargin{:}));
fclose(fid);
end
end
end
end
und ändern Sie einfach eine Codezeile:
clc;
% ... a code
logger = DeepLogger('mymessages.log');
Die obige Methode öffnet einfach eine Datei, hängt eine Nachricht am Ende der Datei an und schließt sie. Im Moment muss ich mich daran erinnern, dass der Name einer Methode LogMessage()
ist, um mit meiner Schnittstelle konsistent zu sein. Es kann jedoch auch alles andere sein. MATLAB kann den Entwickler dazu zwingen, sich an denselben Namen zu halten, indem er abstrakte Klassen verwendet. Angenommen, wir definieren eine gemeinsame Schnittstelle für jeden Logger:
classdef MessageLogger
methods(Abstract=true)
LogMessage(obj, varargin);
end
end
Wenn nun ScreenLogger
und DeepLogger
von dieser Klasse erben, generiert MATLAB einen Fehler, wenn LogMessage()
nicht definiert ist. Abstrakte Klassen helfen beim Aufbau ähnlicher Klassen, die dieselbe Schnittstelle verwenden können.
Im Interesse dieses Beispiels werde ich eine etwas andere Änderung vornehmen. Ich gehe davon aus, dass DeepLogger gleichzeitig eine Protokollierungsnachricht auf einem Bildschirm und in einer Datei durchführt. Da ScreenLogger
bereits Meldungen auf dem Bildschirm protokolliert, erbt DeepLogger
vom ScreenLoggger
, um Wiederholungen zu vermeiden. ScreenLogger
ändert sich bis auf die erste Zeile nicht:
classdef ScreenLogger < MessageLogger
// the rest of previous code
DeepLogger
benötigt jedoch weitere Änderungen in der LogMessage
Methode:
classdef DeepLogger < MessageLogger & ScreenLogger
properties(SetAccess=protected)
FileName
Path
end
methods
function obj = DeepLogger(screenhandler, filename)
[path,filen,ext] = fileparts(filename);
obj.FileName = [filen ext];
pbj.Path = pathn;
obj = obj@ScreenLogger(screenhandler);
end
function LogMessage(obj, varargin)
if ~isempty(varargin)
varargin{1} = num2str(varargin{1});
LogMessage@ScreenLogger(obj, varargin{:});
fid = fopen(obj.fullfname, 'a+t');
fprintf(fid, '%s\n', sprintf(varargin{:}));
fclose(fid);
end
end
end
end
Zunächst initialisiere ich einfach Eigenschaften im Konstruktor. Zweitens, da diese Klasse von ScreenLogger
erbt, ScreenLogger
ich auch dieses Parrent-Objekt initialisieren. Diese Zeile ist noch wichtiger, da der ScreenLogger
Konstruktor einen Parameter benötigt, um sein eigenes Objekt zu initialisieren. Diese Linie:
obj = obj@ScreenLogger(screenhandler);
sagt einfach "den Construor von ScreenLogger aufrufen und mit einem Screenhandler initalisieren". Es ist erwähnenswert, dass ich scrh
als geschützt definiert scrh
. Daher könnte ich auch über DeepLogger
auf diese Eigenschaft DeepLogger
. Wenn die Eigenschaft als privat definiert wurde. Die einzige Möglichkeit, sie zu initialisieren, wäre die Verwendung eines Consuctors.
Eine weitere Änderung ist in Abschnitt methods
. Um Wiederholungen zu vermeiden, rufe ich LogMessage()
von einer übergeordneten Klasse auf, um eine Nachricht auf einem Bildschirm zu protokollieren. Wenn ich etwas ändern musste, um die Bildschirmprotokollierung zu verbessern, muss ich es jetzt an einer Stelle tun. Der Rest-Code ist derselbe wie bei DeepLogger
.
Da diese Klasse auch von einer abstrakten Klasse MessageLogger
erbt, musste ich sicherstellen, dass LogMessage()
in DeepLogger
ebenfalls definiert ist. Die Übernahme von MessageLogger
ist hier etwas schwierig. Ich denke, dass eine Neudefinition von LogMessage
zwingend erforderlich ist - meine Vermutung.
In Bezug auf den Code, in dem ein Logger angewendet wird, kann ich dank einer gemeinsamen Schnittstelle in Klassen sicherstellen, dass die Änderung dieser einen Zeile im gesamten Code keine Probleme verursacht. Die gleichen Meldungen werden auf dem Bildschirm wie zuvor protokolliert, zusätzlich schreibt der Code solche Meldungen in eine Datei.
clc;
% ... a code
logger = DeepLogger(1, 'mylogfile.log');
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
% ... a code
logger.LogMessage('something');
Ich hoffe, diese Beispiele erklärten die Verwendung von Klassen, die Verwendung von Vererbung und die Verwendung von abstrakten Klassen.
PS. Die Lösung für das obige Problem ist eine von vielen. Eine andere, weniger komplexe Lösung wäre, ScreenLoger
zu einer Komponente eines anderen Loggers wie FileLogger
usw. zu machen. ScreenLogger
würde in einer der Eigenschaften enthalten sein. Seine LogMessage
würde einfach LogMessage
des ScreenLogger
und Text auf einem Bildschirm ScreenLogger
. Ich habe einen komplexeren Ansatz gewählt, um eher zu zeigen, wie Klassen in MATLAB arbeiten. Der Beispielcode unten:
classdef DeepLogger < MessageLogger
properties(SetAccess=protected)
FileName
Path
ScrLogger
end
methods
function obj = DeepLogger(screenhandler, filename)
[path,filen,ext] = fileparts(filename);
obj.FileName = [filen ext];
obj.Path = pathn;
obj.ScrLogger = ScreenLogger(screenhandler);
end
function LogMessage(obj, varargin)
if ~isempty(varargin)
varargin{1} = num2str(varargin{1});
obj.LogMessage(obj.ScrLogger, varargin{:}); % <-------- thechange here
fid = fopen(obj.fullfname, 'a+t');
fprintf(fid, '%s\n', sprintf(varargin{:}));
fclose(fid);
end
end
end
end
Konstrukteure
Ein Konstruktor ist eine spezielle Methode in einer Klasse, die aufgerufen wird, wenn eine Instanz eines Objekts erstellt wird. Es ist eine reguläre MATLAB-Funktion, die Eingabeparameter akzeptiert, muss aber auch bestimmten Regeln folgen.
Konstruktoren sind nicht erforderlich, da MATLAB einen Standard erstellt. In der Praxis ist dies jedoch ein Ort, um einen Zustand eines Objekts zu definieren. Zum Beispiel können Eigenschaften durch die Angabe von Attributen eingeschränkt werden. Ein Konstruktor kann dann solche Eigenschaften mit Standardwerten oder benutzerdefinierten Werten initialisieren , die tatsächlich durch Eingabeparameter eines Konstruktors gesendet werden können.
Aufruf eines Konstruktors einer einfachen Klasse
Dies ist eine einfache Klasse Person
.
classdef Person
properties
name
surname
address
end
methods
function obj = Person(name,surname,address)
obj.name = name;
obj.surname = surname;
obj.address = address;
end
end
end
Der Name eines Konstruktors entspricht dem Namen einer Klasse. Folglich werden Konstruktoren mit dem Namen ihrer Klasse aufgerufen. Eine Klasse Person
kann wie folgt angelegt werden:
>> p = Person('John','Smith','London')
p =
Person with properties:
name: 'John'
surname: 'Smith'
address: 'London'
Aufruf eines Konstruktors einer Kindklasse
Klassen können von übergeordneten Klassen geerbt werden, wenn gemeinsame Eigenschaften oder Methoden verwendet werden. Wenn eine Klasse von einer anderen Klasse geerbt wird, ist es wahrscheinlich, dass ein Konstruktor einer übergeordneten Klasse aufgerufen werden muss.
Eine Klasse Member
erbt von einer Klasse Person
, weil Member
der gleichen Eigenschaften wie die Klasse Person verwendet , aber es fügt auch die payment
seine Definition.
classdef Member < Person
properties
payment
end
methods
function obj = Member(name,surname,address,payment)
obj = obj@Person(name,surname,address);
obj.payment = payment;
end
end
end
Ähnlich wie die Klasse Person
wird Member
erstellt, indem der Konstruktor aufgerufen wird:
>> m = Member('Adam','Woodcock','Manchester',20)
m =
Member with properties:
payment: 20
name: 'Adam'
surname: 'Woodcock'
address: 'Manchester'
Ein Konstruktor von Person
benötigt drei Eingabeparameter. Member
muss diese Tatsache respektieren und daher einen Konstruktor der Klasse Person
mit drei Parametern aufrufen. Es wird von der Zeile erfüllt:
obj = obj@Person(name,surname,address);
Das obige Beispiel zeigt den Fall, in dem eine untergeordnete Klasse Informationen für ihre übergeordnete Klasse benötigt. Aus diesem Grund benötigt ein Konstruktor von Member
vier Parameter: drei für die übergeordnete Klasse und einen für sich selbst.