MATLAB Language
Objektorienterad programmering
Sök…
Definiera en klass
En klass kan definieras med classdef
i en .m
fil med samma namn som klassen. Filen kan innehålla classdef
... end
och lokala funktioner för användning inom klassmetoder.
Den mest allmänna definitionen av MATLAB-klassen har följande 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: Klassattribut , Egenskapsattribut , Metodattribut , Eventattribut , Begränsningar av uppräkningsklass .
Exempel klass:
En klass som heter Car
kan definieras i filen Car.m
som
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
Observera att konstruktören är en metod med samma namn som klassen. <En konstruktör är en speciell metod för en klass eller struktur i objektorienterad programmering som initialiserar ett objekt av den typen. En konstruktör är en instansmetod som vanligtvis har samma namn som klassen och kan användas för att ställa in värdena för medlemmarna i ett objekt, antingen som standard eller användardefinierade värden.>
En instans av denna klass kan skapas genom att ringa konstruktören;
>> myCar = Car('Ford', 'Mustang'); //creating an instance of car class
Att ringa drive
ökar körsträckan
>> myCar.mileage
ans =
0
>> myCar.drive(450);
>> myCar.mileage
ans =
450
Värde vs handtagsklasser
Klasser i MATLAB är indelade i två huvudkategorier: värdeklasser och handklasser. Den största skillnaden är att när man kopierar en instans av en värdeklass kopieras de underliggande data till den nya instansen, medan för handtagsklasser pekar den nya instansen på originaldata och ändrar värden i ny instans ändrar dem i originalet. En klass kan definieras som ett handtag genom att ärva från handle
.
classdef valueClass
properties
data
end
end
och
classdef handleClass < handle
properties
data
end
end
sedan
>> 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
Arv från klasser och abstrakta klasser
Friskrivningsklausul: exemplen som presenteras här är endast för att visa användningen av abstrakta klasser och arv och kanske inte nödvändigtvis av praktisk användning. Det finns inte heller någon så sak som polymorf i MATLAB och därför är användningen av abstrakta klasser begränsad. Detta exempel är att visa vem som ska skapa en klass, ärva från en annan klass och tillämpa en abstrakt klass för att definiera ett gemensamt gränssnitt.
Användningen av abstrakta klasser är ganska begränsad i MATLAB men det kan fortfarande vara användbart vid ett par tillfällen.
Låt oss säga att vi vill ha en meddelandelogger. Vi kan skapa en klass som liknar den nedan:
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
Egenskaper och metoder
Kort sagt, egenskaper har ett tillstånd för ett objekt medan metoder är som gränssnitt och definierar åtgärder på objekt.
Fastigheten scrh
skyddas. Det är därför det måste initialiseras i en konstruktör. Det finns andra metoder (getters) för att få tillgång till den här egenskapen men det är inte till exempel exemplet. Egenskaper och metoder kan nås via en variabel som innehåller en referens till ett objekt med hjälp av pricknotation följt av ett namn på en metod eller en egenskap:
mylogger = ScreenLogger(1); % OK
mylogger.LogMessage('My %s %d message', 'very', 1); % OK
mylogger.scrh = 2; % ERROR!!! Access denied
Egenskaper och metoder kan vara offentliga, privata eller skyddade. I det här fallet betyder skyddat att jag kommer att kunna få åtkomst till scrh
från en ärvd klass men inte utanför. Som standard är alla egenskaper och metoder offentliga. Därför kan LogMessage()
fritt användas utanför klassdefinitionen. LogMessage
definierar också ett gränssnitt vilket innebär att detta är vad vi måste ringa när vi vill att ett objekt ska logga in våra anpassade meddelanden.
Ansökan
Låt oss säga att jag har ett skript där jag använder min logger:
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');
Om jag har flera platser där jag använder samma loggare och sedan vill ändra den till något mer sofistikerad, till exempel att skriva ett meddelande i en fil, skulle jag behöva skapa ett annat objekt:
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
och ändra bara en rad i en kod till den här:
clc;
% ... a code
logger = DeepLogger('mymessages.log');
Ovanstående metod öppnar helt enkelt en fil, lägger till ett meddelande i slutet av filen och stänger den. För närvarande, för att vara konsekvent med mitt gränssnitt, måste jag komma ihåg att namnet på en metod är LogMessage()
men det kan lika vara allt annat. MATLAB kan tvinga utvecklaren att hålla sig till samma namn genom att använda abstrakta klasser. Låt oss säga att vi definierar ett gemensamt gränssnitt för alla loggare:
classdef MessageLogger
methods(Abstract=true)
LogMessage(obj, varargin);
end
end
Om både ScreenLogger
och DeepLogger
ärver från denna klass kommer MATLAB att generera ett fel om LogMessage()
inte är definierat. Abstrakta klasser hjälper till att bygga liknande klasser som kan använda samma gränssnitt.
För det här exemplet kommer jag att göra något annorlunda förändring. Jag kommer att anta att DeepLogger kommer att göra både loggningsmeddelande på en skärm och i en fil på samma gång. Eftersom ScreenLogger
redan loggar meddelanden på skärmen kommer jag att ärva DeepLogger
från ScreenLoggger
att undvika upprepning. ScreenLogger
förändras inte alls bortsett från den första raden:
classdef ScreenLogger < MessageLogger
// the rest of previous code
DeepLogger
behöver dock fler förändringar i LogMessage
metoden:
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
Först initialiserar jag helt enkelt egenskaper i konstruktören. För det andra, eftersom denna klass ärver från ScreenLogger
jag också initiera detta parrentobjekt. Den här linjen är ännu viktigare eftersom ScreenLogger
konstruktören kräver en parameter för att initalisera sitt eget objekt. Denna rad:
obj = obj@ScreenLogger(screenhandler);
säger helt enkelt "ring konsulenten till ScreenLogger och initalisera den med en skärmhanterare". Det är värt att notera här att jag har definierat scrh
som skyddad. Därför kunde jag lika få tillgång till den här egenskapen från DeepLogger
. Om fastigheten definierades som privat. Det enda sättet att integrera det skulle vara att använda konsuktorn.
En annan förändring är i avsnittet methods
. För att undvika upprepning ringer jag LogMessage()
från en förälderklass för att logga ett meddelande på en skärm. Om jag var tvungen att ändra något för att göra förbättringar i skärmloggning, måste jag göra det på ett ställe. Restkoden är densamma som den är en del av DeepLogger
.
Eftersom denna klass också ärver från en abstrakt klass MessageLogger
jag tvungen att se till att LogMessage()
inuti DeepLogger
också definieras. Arv från MessageLogger
är lite svårt här. Jag tror att det LogMessage
omdefinition av LogMessage
obligatoriskt - min gissning.
När det gäller koden där en loggare används, tack vare ett gemensamt gränssnitt i klasser, kan jag vara säker på att ändra denna rad i hela koden inte skulle göra några problem. Samma meddelanden kommer att logga in på skärmen som tidigare men dessutom kommer koden att skriva sådana meddelanden till en fil.
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');
Jag hoppas att dessa exempel förklarade användningen av klasser, användningen av arv och användningen av abstrakta klasser.
PS. Lösningen för ovanstående problem är ett av många. En annan lösning, mindre komplex, skulle vara att göra ScreenLoger
till en del av en annan logger som FileLogger
etc. ScreenLogger
skulle hållas i en av egenskaperna. LogMessage
skulle helt enkelt ringa LogMessage
för ScreenLogger
och visa text på en skärm. Jag har valt en mer komplex metod för att snarare visa hur klasser fungerar i MATLAB. Exempelkoden nedan:
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
konstruktörer
En konstruktör är en speciell metod i en klass som kallas när en instans av ett objekt skapas. Det är en vanlig MATLAB-funktion som accepterar ingångsparametrar, men den måste också följa vissa regler .
Konstruktörer krävs inte eftersom MATLAB skapar en standard. I praktiken är det emellertid en plats att definiera ett objekts tillstånd. Till exempel kan egenskaper begränsas genom att ange attribut . Sedan kan en konstruktör initalisera sådana egenskaper som standard eller användardefinierade värden som i själva verket kan skickas med ingångsparametrar för en konstruktör.
Ring en konstruktör av en enkel klass
Detta är en enkel 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
Namnet på en konstruktör är samma som namnet på en klass. Följaktligen kallas konstruktörer med namnet på sin klass. En Person
kan skapas enligt följande:
>> p = Person('John','Smith','London')
p =
Person with properties:
name: 'John'
surname: 'Smith'
address: 'London'
Ring en konstruktör av en barnklass
Klasser kan ärvas från moderklasser om de delar gemensamma egenskaper eller metoder. När en klass ärvs från en annan är det troligt att en konstruktör av en förälderklass måste kallas.
En klass Member
ärver från en klass Person
eftersom Member
använder samma egenskaper som klassen person men det lägger även till payment
till dess 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
På samma sätt som klassen Person
skapas Member
genom att ringa sin konstruktör:
>> m = Member('Adam','Woodcock','Manchester',20)
m =
Member with properties:
payment: 20
name: 'Adam'
surname: 'Woodcock'
address: 'Manchester'
En Person
kräver tre ingångsparametrar. Member
måste respektera detta faktum och därför ringa en konstruktör av klassen Person
med tre parametrar. Det uppfylls av linjen:
obj = obj@Person(name,surname,address);
Exemplet ovan visar fallet när en barnklass behöver information för sin förälderklass. Detta är anledningen till att en konstruktör av Member
kräver fyra parametrar: tre för sin överordnade klass och en för sig själv.