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.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow