MATLAB Language
Object georiënteerd programmeren
Zoeken…
Een klasse definiëren
Een klasse kan worden gedefinieerd met behulp van classdef
in een .m
bestand met dezelfde naam als de klasse. Het bestand kan het bevatten classdef
... end
block en lokale functies voor gebruik binnen methoden van de klasse.
De meest algemene MATLAB-klassedefinitie heeft de volgende structuur:
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-documentatie: Klasseattributen , Eigenschapsattributen , Methodeattributen , Gebeurtenisattributen , Opsommingstype beperkingen .
Voorbeeldklasse:
Een klasse met de naam Car
kan in het bestand Car.m
worden gedefinieerd als
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
Merk op dat de constructor een methode is met dezelfde naam als de klasse. <Een constructor is een speciale methode van een klasse of structuur in objectgeoriënteerd programmeren die een dergelijk object initialiseert. Een constructor is een instantiemethode die meestal dezelfde naam heeft als de klasse en kan worden gebruikt om de waarden van de leden van een object in te stellen, hetzij standaard of door de gebruiker gedefinieerde waarden.
Een exemplaar van deze klasse kan worden gemaakt door de constructor aan te roepen;
>> myCar = Car('Ford', 'Mustang'); //creating an instance of car class
Het aanroepen van de drive
verhoogt de kilometerstand
>> myCar.mileage
ans =
0
>> myCar.drive(450);
>> myCar.mileage
ans =
450
Waarde versus handvatklassen
Klassen in MATLAB zijn verdeeld in twee hoofdcategorieën: waardeklassen en afhandelingsklassen. Het grote verschil is dat bij het kopiëren van een instantie van een waardeklasse, de onderliggende gegevens worden gekopieerd naar de nieuwe instantie, terwijl voor afhandelingsklassen de nieuwe instantie naar de oorspronkelijke gegevens verwijst en de waarden in de nieuwe instantie worden gewijzigd in de oorspronkelijke instantie. Een klasse kan worden gedefinieerd als een handle door over te nemen van de handle
klasse.
classdef valueClass
properties
data
end
end
en
classdef handleClass < handle
properties
data
end
end
vervolgens
>> 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
Overerving van klassen en abstracte klassen
Disclaimer: de hier gepresenteerde voorbeelden zijn alleen bedoeld om het gebruik van abstracte klassen en overerving aan te tonen en hoeven niet noodzakelijkerwijs praktisch te zijn. Ook is er geen zoiets als polymorf in MATLAB en daarom is het gebruik van abstracte klassen beperkt. Dit voorbeeld laat zien wie een klasse moet maken, erven van een andere klasse en een abstracte klasse toepassen om een gemeenschappelijke interface te definiëren.
Het gebruik van abstracte klassen is vrij beperkt in MATLAB, maar het kan nog steeds nuttig zijn bij een paar gelegenheden.
Laten we zeggen dat we een berichtenlogger willen. We kunnen een klasse maken die lijkt op die hieronder:
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
Eigenschappen en methoden
Kortom, eigenschappen hebben de status van een object, terwijl methoden als interface werken en acties op objecten definiëren.
Het eigendom scrh
is beschermd. Daarom moet het in een constructor worden geïnitialiseerd. Er zijn andere methoden (getters) om toegang te krijgen tot deze eigenschap, maar dit voorbeeld valt buiten dit voorbeeld. Eigenschappen en methoden kunnen toegankelijk zijn via een variabele die een verwijzing naar een object bevat met behulp van puntnotatie gevolgd door een naam van een methode of een eigenschap:
mylogger = ScreenLogger(1); % OK
mylogger.LogMessage('My %s %d message', 'very', 1); % OK
mylogger.scrh = 2; % ERROR!!! Access denied
Eigenschappen en methoden kunnen openbaar, privé of beveiligd zijn. In dit geval betekent beschermd dat ik toegang heb tot scrh
van een overgeërfde klasse, maar niet van buitenaf. Standaard zijn alle eigenschappen en methoden openbaar. Daarom kan LogMessage()
vrij worden gebruikt buiten de klassedefinitie. LogMessage
definieert ook een interface, wat betekent dat we dit moeten noemen als we willen dat een object onze aangepaste berichten registreert.
Toepassing
Stel dat ik een script heb waarin ik mijn logger gebruik:
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');
Als ik meerdere plaatsen heb waar ik dezelfde logger gebruik en deze wil veranderen in iets geavanceerder, zoals een bericht in een bestand schrijven, zou ik een ander object moeten maken:
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
en verander gewoon een regel code hierin:
clc;
% ... a code
logger = DeepLogger('mymessages.log');
De bovenstaande methode opent eenvoudig een bestand, voegt een bericht toe aan het einde van het bestand en sluit het. Op dit moment moet ik, om consistent te zijn met mijn interface, onthouden dat de naam van een methode LogMessage()
maar het kan evengoed iets anders zijn. MATLAB kan ontwikkelaar dwingen zich aan dezelfde naam te houden door abstracte klassen te gebruiken. Laten we zeggen dat we een gemeenschappelijke interface definiëren voor elke logger:
classdef MessageLogger
methods(Abstract=true)
LogMessage(obj, varargin);
end
end
Als zowel ScreenLogger
als DeepLogger
van deze klasse overnemen, genereert MATLAB een fout als LogMessage()
niet is gedefinieerd. Abstracte klassen helpen bij het bouwen van vergelijkbare klassen die dezelfde interface kunnen gebruiken.
Omwille van dit voorbeeld zal ik een iets andere verandering aanbrengen. Ik ga ervan uit dat DeepLogger zowel een logboekbericht op een scherm als in een bestand tegelijkertijd uitvoert. Omdat ScreenLogger
al berichten op het scherm logt, ga ik DeepLogger
erven van ScreenLoggger
om herhaling te voorkomen. ScreenLogger
verandert helemaal niet, behalve de eerste regel:
classdef ScreenLogger < MessageLogger
// the rest of previous code
DeepLogger
heeft echter meer wijzigingen nodig in de 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
Ten eerste initialiseer ik eenvoudig eigenschappen in de constructor. Ten tweede, omdat deze klasse van ScreenLogger
erft, moet ik ook dit parrent-object initialiseren. Deze regel is nog belangrijker omdat ScreenLogger
constructor één parameter nodig heeft om zijn eigen object te initialiseren. Deze lijn:
obj = obj@ScreenLogger(screenhandler);
zegt eenvoudig "bel de consructor van ScreenLogger en initialiseer het met een schermhandler". Het is vermeldenswaard dat ik scrh
als beschermd heb gedefinieerd. Daarom had ik ook vanuit DeepLogger
toegang tot deze eigenschap. Als de eigenschap is gedefinieerd als privé. De enige manier om het te initialiseren is door consctor te gebruiken.
Een andere wijziging is in methods
. Nogmaals om herhaling te voorkomen, roep ik LogMessage()
van een bovenliggende klasse op om een bericht op een scherm te loggen. Als ik iets moest veranderen om verbeteringen aan te brengen in de schermregistratie, moet ik dat nu op één plek doen. De DeepLogger
is hetzelfde als het is een onderdeel van DeepLogger
.
Omdat deze klasse ook van een abstracte klasse MessageLogger
erft, moest ik ervoor zorgen dat LogMessage()
in DeepLogger
ook is gedefinieerd. Het overnemen van MessageLogger
is hier een beetje lastig. Ik denk dat het herdefiniëren van LogMessage
verplicht is - denk ik.
Wat betreft de code waarin de logger wordt toegepast, kan ik, dankzij een gemeenschappelijke interface in klassen, er zeker van zijn dat deze regel in de hele code geen problemen zou veroorzaken. Dezelfde berichten worden op het scherm aangemeld als voorheen, maar bovendien schrijft de code dergelijke berichten naar een bestand.
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');
Ik hoop dat deze voorbeelden het gebruik van klassen, het gebruik van overerving en het gebruik van abstracte klassen hebben verklaard.
PS. De oplossing voor het bovenstaande probleem is een van de vele. Een andere, minder complexe oplossing zou zijn om van ScreenLoger
een onderdeel te maken van een andere logger zoals FileLogger
etc. ScreenLogger
zou in een van de eigenschappen worden bewaard. De LogMessage
zou LogMessage
van de ScreenLogger
eenvoudigweg aanroepen en tekst op een scherm weergeven. Ik heb voor een meer complexe aanpak gekozen om liever te laten zien hoe klassen werken in MATLAB. De onderstaande voorbeeldcode:
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
constructors
Een constructor is een speciale methode in een klasse die wordt aangeroepen wanneer een instantie van een object wordt gemaakt. Het is een normale MATLAB-functie die invoerparameters accepteert, maar hij moet ook bepaalde regels volgen.
Constructors zijn niet vereist omdat MATLAB een standaard maakt. In de praktijk is dit echter een plek om een staat van een object te definiëren. Eigenschappen kunnen bijvoorbeeld worden beperkt door attributen op te geven . Vervolgens kan een constructor dergelijke eigenschappen standaard initialiseren of door de gebruiker gedefinieerde waarden die in feite kunnen worden verzonden door invoerparameters van een constructor.
Een constructeur van een eenvoudige klasse bellen
Dit is een eenvoudige 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
De naam van een constructor is hetzelfde als de naam van een klasse. Bijgevolg worden constructeurs bij de naam van zijn klasse genoemd. Een Person
kan als volgt worden gemaakt:
>> p = Person('John','Smith','London')
p =
Person with properties:
name: 'John'
surname: 'Smith'
address: 'London'
Een constructeur van een kinderklasse bellen
Klassen kunnen worden overgenomen van bovenliggende klassen als de gemeenschappelijke eigenschappen of methoden worden gedeeld. Wanneer een klasse van een andere klasse wordt geërfd, is het waarschijnlijk dat een constructor van een bovenliggende klasse moet worden aangeroepen.
Een Member
van de klasse erft van een Person
van de klasse omdat het Member
dezelfde eigenschappen gebruikt als de persoon van de klasse, maar het voegt ook payment
aan zijn definitie.
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
Net als bij de klasse Person
, wordt Member
gemaakt door de constructor aan te roepen:
>> m = Member('Adam','Woodcock','Manchester',20)
m =
Member with properties:
payment: 20
name: 'Adam'
surname: 'Woodcock'
address: 'Manchester'
Een constructor van Person
heeft drie invoerparameters nodig. Member
moet dit feit respecteren en daarom een constructor van de klasse Person
met drie parameters bellen. Het wordt vervuld door de regel:
obj = obj@Person(name,surname,address);
Het bovenstaande voorbeeld toont het geval wanneer een kindklasse informatie nodig heeft voor zijn bovenliggende klasse. Daarom heeft een constructor van Member
vier parameters nodig: drie voor zijn bovenliggende klasse en één voor zichzelf.